Migrating from DAO to ADO

Using ADO with the Jet Provider

 

By Alyssa Henry
March 1999
Table of Contents

Introduction......................................................................................................................................................................... 3

General Differences.......................................................................................................................................................... 4

ADO: Data Manipulation..................................................................................................................................................... 4

ADOX: Data Definition and Security................................................................................................................................. 5

JRO: Replication.................................................................................................................................................................... 6

Getting Started................................................................................................................................................................... 7

Opening a Database.......................................................................................................................................................... 8

Microsoft Jet Databases........................................................................................................................................................ 8

Setting Microsoft Jet Options.............................................................................................................................................. 9

Secured Microsoft Jet Databases...................................................................................................................................... 10

External Databases............................................................................................................................................................. 12

The Current Microsoft Access Database......................................................................................................................... 13

Retrieving and Modifying Data............................................................................................................................... 14

Opening a Recordset........................................................................................................................................................... 14

Navigating Within a Recordset......................................................................................................................................... 16

Finding Records in a Recordset........................................................................................................................................ 19

Filtering and Sorting Data in a Recordset..................................................................................................................... 22

Updating Data in a Recordset........................................................................................................................................... 24

Executing Queries................................................................................................................................................................ 31

Defining and Retrieving a Database's Schema.............................................................................................. 37

Creating a Database........................................................................................................................................................... 37

Retrieving Schema Information......................................................................................................................................... 37

Creating and Modifying Tables........................................................................................................................................ 39

Creating an Index................................................................................................................................................................ 45

Defining Keys and Relationships...................................................................................................................................... 46

Creating and Modifying Queries...................................................................................................................................... 51

Security................................................................................................................................................................................... 57

Changing a Password......................................................................................................................................................... 57

Creating Users and Groups............................................................................................................................................... 59

Adding a User to a Group.................................................................................................................................................. 60

Setting Permissions............................................................................................................................................................. 61

Determining an Object's Owner........................................................................................................................................ 64

Replication........................................................................................................................................................................... 66

Making a Database Replicable........................................................................................................................................ 66

Making Objects Local or Replicable............................................................................................................................... 67

Creating a Replica.............................................................................................................................................................. 69

Creating a Partial Replica................................................................................................................................................ 70

Listing Filters....................................................................................................................................................................... 71

Synchronizing Data............................................................................................................................................................. 72

Listing Synchronization Conflict Tables......................................................................................................................... 73

Miscellaneous.................................................................................................................................................................... 76

Handling Errors................................................................................................................................................................... 76

Using Transactions.............................................................................................................................................................. 77

Compacting a Database..................................................................................................................................................... 80

Refreshing the Cache.......................................................................................................................................................... 81

New Features in ADO, ADOX, and JRO..................................................................................................................... 83

Creatable Recordset Objects............................................................................................................................................. 83

Microsoft Data Links........................................................................................................................................................... 84

User Roster............................................................................................................................................................................ 85

Enhanced Auto-Increment (Counter) Columns............................................................................................................. 86

Replication............................................................................................................................................................................ 87

Obsolete Properties and Methods......................................................................................................................... 90

Conclusion............................................................................................................................................................................ 92

Appendix A: DAO to ADO Quick Reference........................................................................................................... 93

Appendix B: Microsoft Jet 4.0 OLE DB Properties Reference...................................................................... 98

ADO Connection Properties.............................................................................................................................................. 98

ADO Recordset Properties............................................................................................................................................... 111

ADOX Table Properties.................................................................................................................................................... 116

ADOX Column Properties................................................................................................................................................ 117

ADOX Index Properties.................................................................................................................................................... 118

Appendix C: Microsoft Jet 4.0 OLE DB Provider-Defined Property Values.......................................................... 120

Appendix D: Microsoft Jet 4.0 ANSI Reserved Words................................................................................... 122

 

Introduction

This article is a guide to revising code that uses Microsoft Data Access Objects (DAO) into code that uses Microsoft ActiveX Data Objects (ADO). It also guides those who are writing new code using ADO with the OLE DB Provider for Microsoft Jet (Microsoft Jet Provider). It compares the general differences between DAO and ADO and details the mapping of objects, properties, and methods from DAO to ADO. It also highlights functional or semantic differences between similarly named methods or properties.

This article assumes that you have already decided to use ADO instead of DAO to access a Microsoft Jet database, or that you are investigating what it will take to port your code from DAO to ADO. It covers some of the advantages of using ADO over DAO. It describes many features of the Microsoft Jet Provider and demonstrates how to use them with ADO. (The ADO documentation is meant to be provider-neutral, so it lacks much of this information.) This article does not attempt to provide in-depth information on particular objects, properties, or methods. Refer to the online documentation provided with DAO and ADO for specific details on a particular item.


General Differences

Three distinct object models in ADO together provide the functionality found in DAO. These three models are ADO (ADODB), Microsoft ADO Extensions for DDL and Security (ADOX), and Microsoft Jet and Replication Objects (JRO). The functionality of DAO is divided among these three models because many applications will need just one of these subsets of functionality. By splitting the functionality out, applications do not need to incur the overhead of loading additional information into memory unnecessarily. The following sections provide an overview of these three object models.

ADO: Data Manipulation


ADO enables your client applications to access and manipulate data through any OLE DB Provider. ADO contains objects for connecting to a data source and reading, adding, updating, or deleting data.

 

Figure 1. ADO object model

The ADO Connection object defines a session for a user of a particular data source. This differs from DAO, in which the Workspace object defines the session for a user and the Database object defines the data source.

The ADO Command object is similar to the DAO QueryDef object in that both objects can be used to execute an SQL statement against a data source. Likewise, the Recordset object in both ADO and DAO can be used to view the contents of a table or the results from executing an SQL statement.


ADOX: Data Definition and Security


The ADOX model contains objects for data definition (such as tables, views, and indexes) and creating and modifying users and groups. With ADOX an administrator can control database schema and grant and revoke permissions on objects to users and groups.

Figure 2. ADOX object model

The Catalog object is the container for the data definition collections (Tables, Procedures, and Views) and the security collections (Users and Groups). This differs from DAO, in which the Database object contains the data definition collections and the Workspace object contains the security collections. Each Catalog object is associated with only one Connection, whereas a DAO Workspace may contain multiple Databases.

The Table, Index, and Column objects in ADO are roughly equivalent to the TableDef, IndexDef, and Field objects in DAO. Each of these objects also has a standard ADO Properties collection.


Figure 3. ADOX object model

JRO: Replication

The JRO model contains objects, properties, and methods for creating, modifying, and synchronizing replicas. It is designed specifically for use with the Microsoft Jet Provider. Unlike ADO and ADOX, JRO cannot be used with data sources other than Microsoft Jet databases.

The primary object in the JRO model is the Replica object. The Replica object is used to create new replicas, to retrieve and modify properties of an existing replica, and to synchronize changes with other replicas. This differs from DAO, in which the Database object is used for these tasks.


JRO also includes a JetEngine object for two specific Microsoft Jet database engine features: compacting the database and refreshing data from the memory cache.


Figure 4. JRO object model

 


Getting Started

To run the code examples in this article, you need references to the ADO, ADOX, and JRO type libraries in your database or project. By default, new Microsoft Access 2000 databases have a reference to ADO. However, to run these samples you'll need to add references to ADOX and JRO. If you converted an existing database to Access 2000 or are programming in Microsoft Visual Basic® or some other application, you will need to include all of the references yourself.

To add these references in Access 2000

1.        Open a module.

2.        From the Tools menu, select References…

3.        From the list, select Microsoft ActiveX Data Objects 2.1 Library.

4.        From the list, select Microsoft ADO Ext. 2.1 for DDL and Security.

5.        From the list, select Microsoft Jet and Replication Objects 2.1 Library.

6.        Click OK.

To add these references in Visual Basic

1.        Open a project.

2.        From the Project menu, select References…

3.        From the list, select Microsoft ActiveX Data Objects 2.1 Library.

4.        From the list, select Microsoft ADO Ext. 2.1 for DDL and Security.

5.        From the list, select Microsoft Jet and Replication Objects 2.1 Library.

6.        Click OK.

If you include references to both ADO and DAO in the same project, you need to explicitly specify which library to use when declaring objects because DAO and ADO include several objects with the same names. For example, both models include a Recordset object, so the following code is ambiguous:

Dim rst as Recordset

To specify which object model you want to use, include a qualifier as shown:

Dim rstADO As ADODB.Recordset

Dim rstDAO As DAO.Recordset

If the qualifier is omitted, Visual Basic for Applications will choose the object from the model that is referenced first. So if your list of references is ordered as follows in the References dialog box, an object declared as Recordset with no qualifier would be a DAO Recordset.

Visual Basic for Applications

Microsoft DAO 3.6 Object Library

Microsoft ActiveX Data Objects 2.1 Library

Microsoft ADO Ext. 2.1 for DDL and Security

Microsoft Jet and Replication Objects 2.1 Library

Note The code samples given in this article are intended for illustration only. They do represent the good techniques for sample code, but not necessarily for production code. Production-level code should account for error handling, object creation and reference counting, connection management, performance tuning, and so on. For example, the following technique for object creation is used in the samples:

Dim rst As New ADODB.Recordset

For better control over object creation and maximum performance, production code should use this technique instead:

Dim rst As ADODB.Recordset

Set rst = New ADODB.Recordset

 


Opening a Database

Generally, one of the first steps in writing an application to access data is to open the data source. When using the Microsoft Jet database engine, you can open Microsoft Jet databases, other external data sources such as Microsoft Excel, Paradox, and dBASE with Microsoft Jet's ISAM components, and ODBC data sources.

Microsoft Jet Databases

The Microsoft Jet Provider can open Microsoft Jet 4.0 databases as well as databases created with previous versions of the Microsoft Jet database engine. These examples use only Microsoft Jet 4.0 databases.

The following code demonstrates how to open a Microsoft Jet database for shared, updatable access. Then the code immediately closes the database because this code is for demonstration purposes.

DAO

Sub DAOOpenJetDatabase()

 

   Dim db As DAO.Database

 

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   db.Close

 

End Sub

ADO

Sub ADOOpenJetDatabase()

 

   Dim cnn As New ADODB.Connection

 

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

   cnn.Close

 

End Sub

These two code listings for opening a database look somewhat different, but are not all that dissimilar. Aside from the fact that the objects have different names, the major difference is the format of the string passed to the method that opens the database.

The ADO connection string in this example has two parts: the provider tag and the data source tag. The provider tag indicates which OLE DB Provider to use, and the data source tag indicates which database to open. With DAO it is assumed that you want to use Microsoft Jet, whereas with ADO you must explicitly specify that you want to use Microsoft Jet.

By default, both DAO and ADO open a database for shared updatable access, when using the Microsoft Jet Provider. However, there may be times when you want to open the database exclusively or in read-only mode.

The following code listings show how to open (and then close) a shared, read-only database using DAO and ADO.

DAO

Sub DAOOpenJetDatabaseReadOnly()

 

   Dim db As DAO.Database

 

   ' Open shared, read-only.

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb", False, True)

   db.Close

 

End Sub

ADO

Sub ADOOpenJetDatabaseReadOnly()

 

   Dim cnn As New ADODB.Connection

 

   ' Open shared, read-only

   cnn.Mode = adModeRead

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

   cnn.Close

 

End Sub

In the DAO listing the second two parameters to the OpenDatabase method indicate exclusive and read-only access respectively. In the ADO listing the Connection object's Mode property is set to the read-only constant (adModeRead). By default, ADO connections are opened for shared, updatable access unless another mode is set (for example, adModeShareExclusive).

Alternatively, the ADO listing could have been written in a single line of code as follows:

Sub ADOOpenJetDatabaseExclusive()

 

   Dim cnn As New ADODB.Connection

 

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;Mode=" & adModeRead

   cnn.Close

 

End Sub

In this listing the Mode property was specified as a part of the connection string to the Open method rather than as a property of the Connection object. In ADO you can set connection properties as a property or string them together with other properties to create the connection string. Even provider-specific properties (prefixed by "Jet OLEDB:" for Microsoft Jet–specific properties) can be set as part of the connection string or with the Connection object's Properties collection. For a description of the available properties, see "Appendix B: Microsoft Jet 4.0 OLE DB Properties Reference" later in this article.

Setting Microsoft Jet Options

The Microsoft Jet database engine exposes a number of settable options that will dictate how the engine will behave. These options often have a direct impact on performance. By default when the Microsoft Jet database engine is initialized, it uses the values set in the Windows registry under the \HKEY_LOCAL_MACHINES\Software\Microsoft\Jet key. At run time, it is possible to temporarily override these settings. In ADO these values are set as part of the connection string.

The following listings demonstrate how to override the Page Timeout setting of the engine and open a database using that setting.

DAO

Sub DAOSetJetDBOption()

 

   Dim db As DAO.Database

 

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

   DBEngine.SetOption dbPageTimeout, 4000

   db.Close

 

End Sub

ADO

Sub ADOSetJetDBOption()

 

   Dim cnn As New ADODB.Connection

 

   cnn.Provider = "Microsoft.Jet.OLEDB.4.0;"

   cnn.Open ".\NorthWind.mdb"

   cnn.Properties("Jet OLEDB:Page Timeout") = 4000

   cnn.Close

 

End Sub

With DAO you use the SetOption method to set the values for these database settings. There is no corresponding GetOption method to retrieve the values. With ADO you use a property in the Connection object's Properties collection. You can read the value of the property using ADO; however, this value is not accurate unless you have previously set the value for the property. For example, the Jet OLEDB:Page Timeout property will return the value 0 prior to setting this property even though the value defined for this property in the HKEY_LOCAL_MACHINE\Software\Microsoft\Jet\4.0\Engines\Jet 4.0\PageTimeout registry key is actually 5000.

Another minor difference between ADO and DAO is that with ADO the Connection must be opened before these properties are available. With DAO these properties can be set on the DBEngine object prior to opening the database.

As shown in the listings, you can optionally set the provider in the Provider property, rather than in the connection string. The "Data Source=" section of the connection string can also be omitted, and ADO will assume this is the default value for the path in the connection string. This is simply an alternative method of opening a connection; with ADO there are sometimes many equally valid ways to accomplish a task. Later in this article, the section "Opening a Database with User-Level Security" explains a scenario in which it is required that you indicate the provider in the Provider property rather than in the connection string.

The following table lists the values that can be set with DAO's SetOption method and the corresponding property to use with ADO.

DAO constant

ADO property

dbPageTimeout

Jet OLEDB:Page Timeout

dbSharedAsyncDelay

Jet OLEDB:Shared Async Delay

dbExclusiveAsyncDelay

Jet OLEDB:Exclusive Async Delay

dbLockRetry

Jet OLEDB:Lock Retry

dbUserCommitSync

Jet OLEDB:User Commit Sync

dbImplicitCommitSync

Jet OLEDB:Implicit Commit Sync

dbMaxBufferSize

Jet OLEDB:Max Buffer Size

dbMaxLocksPerFile

Jet OLEDB:Max Locks Per File

dbLockDelay

Jet OLEDB:Lock Delay

dbRecycleLVs

Jet OLEDB:Recycle Long-Valued Pages

dbFlushTransactionTimeout

Jet OLEDB:Flush Transaction Timeout

Secured Microsoft Jet Databases

Microsoft Jet databases can be secured in one of two ways: with either share-level security or user-level security. With share-level security, the database is secured with a password. Anyone attempting to open the database must specify the correct database password. With user-level security, each user is assigned a user name and password to open the database. Microsoft Jet uses a separate workgroup information file, typically named "system.mdw" to store user information and passwords. See the section "Security" for more information about creating and using secured Microsoft Jet databases.

Share-Level (Password Protected) Databases

The following listings demonstrate how to open a Microsoft Jet database that has been secured at the share level.

DAO

Sub DAOOpenDBPasswordDatabase()

 

   Dim db As DAO.Database

 

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb", _

      False, False, ";pwd=password")

   db.Close

 

End Sub

ADO

Sub ADOOpenDBPasswordDatabase()

 

   Dim cnn As New ADODB.Connection

 

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;" & _

      "Jet OLEDB:Database Password=password;"

   cnn.Close

 

End Sub

In DAO the Connect parameter of the OpenDatabase method sets the database password when opening a database. With ADO the Microsoft Jet Provider connection property Jet OLEDB:Database Password sets the password instead.

Opening a Database with User-Level Security

These next listings demonstrate how to open a database that is secured at the user level using a workgroup information file named "system.mdw."

DAO

Sub DAOOpenSecuredDatabase()

 

   Dim wks As DAO.Workspace

   Dim db As DAO.Database

 

   DBEngine.SystemDB = _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

 

   Set wks = DBEngine.CreateWorkspace("", "Admin", "")

   Set db = wks.OpenDatabase(".\NorthWind.mdb")

 

   db.Close

   wks.Close

 

End Sub

ADO

Sub ADOOpenSecuredDatabase()

 

   Dim cnn As New ADODB.Connection

 

   cnn.Provider = "Microsoft.Jet.OLEDB.4.0;"

   cnn.Properties("Jet OLEDB:System database") = _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

 

   cnn.Open "Data Source=.\NorthWind.mdb;User Id=Admin;Password=;"

   cnn.Close

 

End Sub

In ADO a Microsoft Jet Provider-specific connection property, Jet OLEDB:System database, specifies the system database. This is equivalent to setting the DBEngine object's SystemDB property before opening a database using DAO.

Notice that, in this example, the Provider property is set as a property of the Connection object rather than as part of the ConnectionString argument to the Open method. That is because before you can reference provider-specific properties from the Connection object's Properties collection, it is necessary to indicate which provider you are using. If the first line of code had been omitted, error 3265 (adErrItemNotFound), "ADO could not find the object in the collection corresponding to the name or ordinal reference requested by the application," would have occurred when trying to set the value for the Jet OLEDB:System database property.

Note that in both DAO and ADO setting the system database may not be necessary. You may omit the code that sets the system database if you want to use the current Microsoft Jet workgroup information file as specified in the SystemDB key in the Microsoft Jet registry entries. See the book Microsoft Jet Database Engine Programmer's Guide (available from Microsoft Press Online at ) for more information about Microsoft Jet Security.

External Databases

The Microsoft Jet database engine can be used to access other database files, spreadsheets, and textual data stored in tabular format through installable ISAM drivers.

The following listings demonstrate how to open a Microsoft Excel 2000 spreadsheet first using DAO, and then using ADO and the Microsoft Jet Provider.

DAO

Sub DAOOpenISAMDatabase()

 

   Dim db As DAO.Database

 

   Set db = DBEngine.OpenDatabase(".\Sales.xls", _

      False, False, "Excel 8.0;")

 

   db.Close

 

End Sub

ADO

Sub ADOOpenISAMDatabase()

 

   Dim cnn As New ADODB.Connection

 

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\Sales.xls" & _

      ";Extended Properties=Excel 8.0;"

 

   cnn.Close

 

End Sub

The DAO and ADO code for opening an external database is similar. In both examples, the name of the external file (Sales.xls) is used in place of a Microsoft Jet database file name. With both DAO and ADO you must also specify the type of external database you are opening—in this case, an Excel 2000 spreadsheet. With DAO the database type is specified in the Connect argument of the OpenDatabase method. The database type is specified in the Extended Properties property of the Connection with ADO. The following table lists the strings to use to specify which ISAM to open.

Database

String

dBASE III

dBASE III;

dBASE IV

dBASE IV;

dBASE 5

dBASE 5.0;

Paradox 3.x

Paradox 3.x;

Paradox 4.x

Paradox 4.x;

Paradox 5.x

Paradox 5.x;

Excel 3.0

Excel 3.0;

Excel 4.0

Excel 4.0;

Excel 5.0/Excel 95

Excel 5.0;

Excel 97

Excel 97;

Excel 2000

Excel 8.0;

HTML Import

HTML Import;

HTML Export

HTML Export;

Text

Text;

ODBC

ODBC;
DATABASE=database;
UID=user;
PWD=password;
DSN=datasourcename;

Note that if you are migrating from DAO 3.5 or earlier with the FoxPro ISAM to ADO with the Microsoft Jet Provider, you will need to use Microsoft Visual FoxPro® ODBC Driver because Microsoft Jet 4.0 does not support the FoxPro ISAM.

The Current Microsoft Access Database

When you open Microsoft Access, you are opening a Microsoft Jet database. When writing code within Access, you may often want to use the same connection to Microsoft Jet as Access is using. To allow you to do this, Microsoft Access 2000 exposes two mechanisms: CurrentDB() and CurrentProject.Connection allow you to get a DAO Database object and an ADO Connection object, respectively, for the database Access currently has open.

The following listings demonstrate how to get a reference to the database currently open in Microsoft Access.

DAO

Sub DAOGetCurrentDatabase()

 

   Dim db As DAO.Database

 

   Set db = CurrentDb()

 

End Sub

ADO

Sub ADOGetCurrentDatabase()

 

   Dim cnn As ADODB.Connection

 

   Set cnn = CurrentProject.Connection

 

End Sub

 


Retrieving and Modifying Data

Both DAO and ADO include a Recordset object that is the primary object used for retrieving and modifying data. A Recordset object represents a set of records in a table or a set of records that are a result of a query.

The Recordset object contains a Fields collection that contains Field objects, each of which represents a single column of data within the Recordset.

Opening a Recordset

Like DAO, ADO Recordset objects can be opened from several different objects. In ADO a Recordset can be opened with the Connection object Execute method, the Command object Execute method, or the Recordset object Open method. ADO Recordset objects cannot be opened directly from Table, Procedure, or View objects. ADO Recordset objects opened with the Execute method are always forward-only, read-only recordsets. If you need to be able to scroll or update data within the Recordset you must use the Recordset object Open method.

The CursorType, LockType, and Options parameters of the Open method determine the type of Recordset that is returned. The following table shows how the parameters to the DAO Recordset object Open method can be mapped to ADO properties.

DAO Recordset type

ADO Recordset properties
or parameters

dbOpenDynaset

CursorType=adOpenDynamic

dbOpenSnapshot

CursorType=adOpenStatic

dbOpenForwardOnly

CursorType=adOpenForwardOnly

dbOpenTable

CursorType=adOpenDynamic, Options=adCmdTableDirect

 

DAO Recordset Options
values

ADO Recordset properties

dbAppendOnly

Properties("Append-Only Rowset")

dbSQLPassThrough

Properties("Jet OLEDB:ODBC Pass-Through Statement")

dbSeeChanges

Not currently supported.

dbDenyWrite

Not currently supported.

dbDenyRead

Not currently supported.

dbInconsistent

Properties("Jet OLEDB:Inconsistent") = True

dbConsistent

Properties("Jet OLEDB:Inconsistent") = False

 

DAO Recordset LockType
values

ADO Recordset LockType
values

dbReadOnly

adLockReadOnly

dbPessimistic

adLockPessimistic

dbOptimistic

adLockOptimistic

The Microsoft Jet Provider does not support a number of combinations of CursorType and LockType—for example, CursorType=adOpenDynamic and LockType=adLockOptimistic. If you specify an unsupported combination, ADO will pass your request to the Microsoft Jet Provider, which will then degrade to a supported CursorType or LockType. Use the CursorType and LockType properties of the Recordset once it is opened to determine what type of Recordset was created.

The following listings demonstrate how to open a forward-only, read-only Recordset, and then prints the values of each field.

DAO

Sub DAOOpenRecordset()

 

   Dim db As DAO.Database

   Dim rst As DAO.Recordset

   Dim fld As DAO.Field

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Open the Recordset

   Set rst = db.OpenRecordset _

      ("SELECT * FROM Customers WHERE Region = 'WA'", _

      dbOpenForwardOnly, dbReadOnly)

 

   ' Print the values for the fields in

   ' the first record in the debug window

   For Each fld In rst.Fields

      Debug.Print fld.Value & ";";

   Next

 

   Debug.Print

 

   ' Close the recordset

   rst.Close

 

End Sub

ADO

Sub ADOOpenRecordset()

 

   Dim cnn As New ADODB.Connection

   Dim rst As New ADODB.Recordset

   Dim fld As ADODB.Field

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the forward-only,

   ' read-only recordset

   rst.Open _

      "SELECT * FROM Customers WHERE Region = 'WA'", _

      cnn, adOpenForwardOnly, adLockReadOnly

 

   ' Print the values for the fields in

   ' the first record in the debug window

   For Each fld In rst.Fields

      Debug.Print fld.Value & ";";

   Next

 

   Debug.Print

 

   ' Close the recordset

   rst.Close

 

End Sub

In the preceding DAO and ADO code, the Recordset is opened and then the data in the first record of the Recordset is printed to the Debug window by iterating through each field in the Fields collection and printing its Value.

Using Client Cursors

ADO Recordset objects have an additional property, CursorLocation, not found in DAO, which affects the functionality and performance of the Recordset. This property has two valid values: adUseServer and adUseClient. The default is adUseServer, which indicates that the provider's or data source's cursors should be used.

When the CursorLocation property is set to adUseClient, ADO will invoke the Microsoft Cursor Service for OLE DB to create the Recordset. The Cursor Service retrieves data from the underlying data provider using a forward-only, read-only cursor and stores all of the data in its own cache on the client. When data is requested through ADO, the Cursor Service returns the data from its own cache rather than passing the request down to the provider. This often results in fairly significant performance gains when the underlying data source is on a remote server as is often the case with Microsoft SQL Server™. However, when the data is stored in a local Microsoft Jet database, this can result in fairly significant performance degradation as the data is being cached twice on the client, once in Microsoft Jet and once in the Cursor Service.

While there may be a performance penalty for using the Cursor Service, it does provide some functionality found in DAO that is not currently exposed in the Microsoft Jet Provider. For example, adUseClient must be specified for CursorLocation in order to sort an existing Recordset. (See the section "Filtering and Sorting Data in a Recordset" for more information about how to use the Cursor Service to sort a Recordset.)

When developing your application, you'll generally want to specify adUseServer as the CursorLocation to get performance and functionality similar to DAO. However, in the few cases where the Microsoft Jet Provider does not provide the functionality needed, consider using client cursors.

Navigating Within a Recordset

A Recordset object has a current position. The position may be before the first record (BOF), after the last record (EOF), or on a specific record within the Recordset. When retrieving information with the Field object, the information always pertains to the record at the current position.

Moving To Another Record

Both DAO and ADO contain several methods for moving from one record to another. These methods are Move, MoveFirst, MoveLast, MoveNext, and MovePrevious.

The following listings demonstrate how to use the MoveNext method to iterate through all of the records in the Recordset.

DAO

Sub DAOMoveNext()

 

   Dim db As DAO.Database

   Dim rst As DAO.Recordset

   Dim fld As DAO.Field

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Open the Recordset

   Set rst = db.OpenRecordset _

      ("SELECT * FROM Customers WHERE Region = 'WA'", _

      dbOpenForwardOnly, dbReadOnly)

 

   ' Print the values for the fields in

   ' the first record in the debug window

   Do Until rst.EOF

      For Each fld In rst.Fields

         Debug.Print fld.Value & ";";

      Next

      Debug.Print

      rst.MoveNext

   Loop

 

   ' Close the recordset

   rst.Close

 

End Sub

ADO

Sub ADOMoveNext()

 

   Dim cnn As New ADODB.Connection

   Dim rst As New ADODB.Recordset

   Dim fld As ADODB.Field

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" _

      & "Data Source=.\NorthWind.mdb;"

 

   ' Open the forward-only,

   ' read-only recordset

   rst.Open _

      "SELECT * FROM Customers WHERE Region = 'WA'", _

      cnn, adOpenForwardOnly, adLockReadOnly

 

   ' Print the values for the fields in

   ' the first record in the debug window

   Do Until rst.EOF

      For Each fld In rst.Fields

         Debug.Print fld.Value & ";";

      Next

      Debug.Print

      rst.MoveNext

   Loop

 

   ' Close the recordset

   rst.Close

 

End Sub

Notice that the code for iterating through the Recordset in DAO and ADO is identical.

ADO Shortcuts to Display and Open Recordsets

In the previous example, the ADO code could be rewritten to use the Recordset object's GetString method to print the data to the Debug window. This method returns a formatted string containing data from the records in the Recordset. Using GetString, the While loop in the previous ADO example could be replaced with the single line:

   Debug.Print rst.GetString(adClipString, , ";")

This method is handy for debugging as well as populating grids and other controls that allow you to pass in a formatted string representing the data. GetString is also faster than looping through the Recordset and generating the string with Visual Basic for Applications code.

The ADO example could also have been rewritten more concisely by using the Recordset object's Open method's ActiveConnection parameter to specify the connection string rather than first opening a Connection object and then passing that object in as the ActiveConnection. The Recordset object's Open method call would look like this:

   rst.Open _

      "SELECT * FROM Customers WHERE Region = 'WA'", _

      "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;", _

      adOpenForwardOnly, adLockReadOnly

Internally, these two mechanisms are essentially the same. When you pass a connection string to the Recordset object's Open method (rather than assigning a Connection object to the Recordset object's ActiveConnection property), ADO creates a new, internal Connection object. If you plan on opening more than one Recordset from a given data source, or opening Command or Catalog objects, create a Connection object and use that object for the ActiveConnection. This will reduce the amount of resources consumed and increase performance.

Determining Current Position

When working with records in a Recordset it may be useful to know what the record number of the current record is. Both ADO and DAO have an AbsolutePosition property that can be used to determine the record number. The following code listings demonstrate how to use the AbsolutePosition property in both DAO and ADO.

DAO

Sub DAOGetCurrentPosition()

 

   Dim db As DAO.Database

   Dim rst As DAO.Recordset

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Open the Recordset

   Set rst = db.OpenRecordset("SELECT * FROM Customers", _

      dbOpenDynaset)

 

   ' Print the absolute position

   Debug.Print rst.AbsolutePosition

 

   ' Move to the last record

   rst.MoveLast

 

   ' Print the absolute position

   Debug.Print rst.AbsolutePosition

 

   ' Close the recordset

   rst.Close

 

End Sub

ADO

Sub ADOGetCurrentPosition()

 

   Dim cnn As New ADODB.Connection

   Dim rst As New ADODB.Recordset

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the recordset

   rst.CursorLocation = adUseClient

   rst.Open "SELECT * FROM Customers", cnn, adOpenKeyset, _

      adLockOptimistic, adCmdText

 

   ' Print the absolute position

   Debug.Print rst.AbsolutePosition

 

   ' Move to the last record

   rst.MoveLast

 

   ' Print the absolute position

   Debug.Print rst.AbsolutePosition

 

   ' Close the recordset

   rst.Close

 

End Sub

The ADO and DAO code for determining the current position within the Recordset looks very similar. However, note that the results printed to the Debug window are different. With DAO the AbsolutePosition property is zero-based; the first record in the Recordset has an AbsolutePosition of 0. With ADO the AbsolutePosition property is one-based; the first record in the Recordset has an AbsolutePosition of 1.

Note that in the previous ADO code example the CursorLocation property is set to adUseClient. If the CursorLocation is not specified or is set to adUseServer, the AbsolutePosition property will return adUnknown (-1) because the Microsoft Jet Provider does not support retrieving this information. See the section "Using Client Cursors" for more information about using the CursorLocation property.

In addition to the AbsolutePosition property, DAO also has a PercentPosition property that returns a percentage representing the approximate position of the current record within the Recordset. ADO does not have a property or method that provides the functionality equivalent to DAO's PercentPosition property.

Finding Records in a Recordset

Both DAO and ADO have two mechanisms for locating a record in a Recordset: Find and Seek. With both mechanisms you specify criteria to use to locate a matching record. In general, for equivalent types of searches, Seek provides better performance than Find. However, because Seek uses an underlying index to locate the record, it is limited to Recordset objects that have associated indexes. For Microsoft Jet databases only, Recordset objects based on a table (DAO dbOpenTable, ADO adCmdTableDirect) with an index support Seek.

Using the Find Method

The following listings demonstrate how to locate a record using Find.

DAO

Sub DAOFindRecord()

 

   Dim db As DAO.Database

   Dim rst As DAO.Recordset

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Open the Recordset

   Set rst = db.OpenRecordset("Customers", dbOpenDynaset)

 

   ' Find the first customer whose country is USA

   rst.FindFirst "Country = 'USA'"

 

   ' Print the customer id's of all customers in the USA

   Do Until rst.NoMatch

      Debug.Print rst.Fields("CustomerId").Value

      rst.FindNext "Country = 'USA'"

   Loop

 

   ' Close the recordset

   rst.Close

 

End Sub

ADO

Sub ADOFindRecord()

 

   Dim cnn As New ADODB.Connection

   Dim rst As New ADODB.Recordset

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the recordset

   rst.Open "Customers", cnn, adOpenKeyset, adLockOptimistic

 

   ' Find the first customer whose country is USA

   rst.Find "Country='USA'"

 

   ' Print the customer id's of all customers in the USA

   Do Until rst.EOF

      Debug.Print rst.Fields("CustomerId").Value

      rst.Find "Country='USA'", 1

   Loop

 

   ' Close the recordset

   rst.Close

 

End Sub

DAO includes four find methods: FindFirst, FindLast, FindNext, and FindPrevious. You choose which method to use based on the point from which you want to start searching (beginning, end, or current record) and in which direction you want to search (forward or backward).

ADO has a single method: Find. Searching always begins from the current record. The Find method has parameters that allow you to specify the search direction as well as an offset from the current record at which to begin searching (SkipRows). The following table shows how to map the four DAO methods to the equivalent functionality in ADO.

DAO method

ADO Find
with SkipRows

ADO search direction

FindFirst

0

adSearchForward (if not currently positioned on the first record, call MoveFirst before Find)

FindLast

0

adSearchBackward (if not currently positioned on the last record, call MoveLast before Find)

FindNext

1

adSearchForward

FindPrevious

1

adSearchBackward

DAO and ADO require a different syntax for locating records based on a Null value. In DAO if you want to find a record that has a Null value you use the following syntax:

"ColumnName Is Null"

or, to find a record that does not have a Null value for that column:

"ColumnName Is Not Null"

ADO, however, does not recognize the Is operator. You must use the = or <> operators instead. So the equivalent ADO criteria would be:

"ColumnName = Null"

or:

"ColumnName <> Null"

So far, each of the criteria shown in the preceding examples are based on a value for a single column. However, with DAO, the Criteria parameter is like the WHERE clause in an SQL statement and can contain multiple columns and compare operators within the criteria.

This is not the case with ADO. The ADO Criteria parameter is a string containing a single column name, comparison operator, and value to use in the search. If you need to find a record based on multiple columns, use the Filter property (see the section "Filtering and Sorting Data in a Recordset") to create a view of the Recordset that only contains those records matching the criteria.

DAO and ADO behave differently if a record that meets the specified criteria is not found. DAO sets the NoMatch property to True and the current record is not defined. If ADO does not find a record that meets the criteria, the current record is positioned either before the beginning of the Recordset if searching forward (adSearchForward) or after the end of the Recordset if searching backward (adSearchBackward). Use the BOF or EOF properties as appropriate to determine whether a match was found.

Using the Seek Method

The following listings demonstrate how to locate a record using Seek.

DAO

Sub DAOSeekRecord()

 

   Dim db As DAO.Database

   Dim rst As DAO.Recordset

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Open the Recordset

   Set rst = db.OpenRecordset("Order Details", dbOpenTable)

 

   ' Select the index used to order the data in the recordset

   rst.Index = "PrimaryKey"

 

   ' Find the order where OrderId = 10255 and ProductId = 16

   rst.Seek "=", 10255, 16

 

   ' If a match is found print the quantity of the order

   If Not rst.NoMatch Then

      Debug.Print rst.Fields("Quantity").Value

   End If

 

   ' Close the recordset

   rst.Close

 

End Sub

ADO

Sub ADOSeekRecord()

 

   Dim cnn As New ADODB.Connection

   Dim rst As New ADODB.Recordset

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the recordset

   rst.Open "Order Details", cnn, adOpenKeyset, adLockReadOnly, _

      adCmdTableDirect

 

   ' Select the index used to order the data in the recordset

   rst.Index = "PrimaryKey"

 

   ' Find the order where OrderId = 10255 and ProductId = 16

   rst.Seek Array(10255, 16), adSeekFirstEQ

 

   ' If a match is found print the quantity of the order

   If Not rst.EOF Then

   Debug.Print rst.Fields("Quantity").Value

   End If

 

   ' Close the recordset

   rst.Close

 

End Sub

Because Seek is based on an index, it is important to specify an index before searching. In the previous example, this is not strictly necessary because Microsoft Jet will use the primary key if an index is not specified.

In the ADO example, the Visual Basic for Applications Array function is used when specifying a value for more than one column as part of the KeyValues parameter. If only one value is specified, it is not necessary to use the Array function.

As with the Find method, use the NoMatch property with DAO to determine whether a matching record was found. Use the BOF and EOF properties as appropriate with ADO.

The Seek method will work correctly only for Microsoft Jet 4.0 databases. It will fail with a run-time error for all earlier formats, even if you use the Microsoft Jet 4.0 database engine to open the database. This will cause a problem if the application is written to support older database formats. If so, use the Supports method of the Recordset object to determine whether the Seek method is available for the open Recordset. However, if all client applications use the newer format, this check is unnecessary. Use either Microsoft Access 2000 or the CompactDatabase method to convert older databases to the newer format.

Filtering and Sorting Data in a Recordset

In general, when using ADO with the Microsoft Jet Provider you will have better performance if you filter and sort data by specifying an SQL WHERE or ORDER BY clause in the SQL statement or stored query used to open the Recordset.

Using the Filter Property

The following listings demonstrate how to use the Filter property.

DAO

Sub DAOFilterRecordset()

 

   Dim db As DAO.Database

   Dim rst As DAO.Recordset

   Dim rstFlt As DAO.Recordset

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Open the Recordset

   Set rst = db.OpenRecordset("Customers", dbOpenDynaset)

 

   ' Set the Filter to be used for subsequent recordsets

   rst.Filter = "Country='USA' And Fax Is Not Null"

 

   ' Open the filtered recordset

   Set rstFlt = rst.OpenRecordset()

   Debug.Print rstFlt.Fields("CustomerId").Value

 

   ' Close the recordsets

   rst.Close

   rstFlt.Close

 

End Sub

ADO

Sub ADOFilterRecordset()

 

   Dim cnn As New ADODB.Connection

   Dim rst As New ADODB.Recordset

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the recordset

   rst.Open "Customers", cnn, adOpenKeyset, adLockOptimistic

 

   ' Filter the recordset to include only those customers in

   ' the USA that have a fax number

   rst.Filter = "Country='USA' And Fax <> Null"

   Debug.Print rst.Fields("CustomerId").Value

 

   ' Close the recordset

   rst.Close

 

End Sub

The DAO and ADO Filter properties are used slightly differently. With DAO the Filter property specifies a filter to be applied to any subsequently opened Recordset objects based on the Recordset for which you have applied the filter. With ADO the Filter property applies to the Recordset to which you applied the filter. The ADO Filter property allows you to create a temporary view that can be used to locate a particular record or set of records within the Recordset. When a filter is applied to the Recordset, the RecordCount property reflects just the number of records within the view. The filter can be removed by setting the Filter property to adFilterNone.

Using the Sort Method

The following listings demonstrate how to sort records with the Sort method.

DAO

Sub DAOSortRecordset()

 

   Dim db As DAO.Database

   Dim rst As DAO.Recordset

   Dim rstSort As DAO.Recordset

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Open the Recordset

   Set rst = db.OpenRecordset("Customers", dbOpenDynaset)

 

   ' Sort the recordset based on Country and Region both in

   ' ascending order

   rst.Sort = "Country, Region"

 

   ' Open the sorted recordset

   Set rstSort = rst.OpenRecordset()

   Debug.Print rstSort.Fields("CustomerId").Value

 

   ' Close the recordsets

   rst.Close

   rstSort.Close

 

End Sub

ADO

Sub ADOSortRecordset()

 

   Dim cnn As New ADODB.Connection

   Dim rst As New ADODB.Recordset

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the recordset

   rst.CursorLocation = adUseClient

   rst.Open "Customers", cnn, adOpenKeyset, adLockOptimistic

 

   ' Sort the recordset based on Country and Region both in

   ' ascending order

   rst.Sort = "Country, Region"

   Debug.Print rst.Fields("CustomerId").Value

 

   ' Close the recordset

   rst.Close

 

End Sub

Like the Filter property, the DAO and ADO Sort properties differ in that the DAO Sort applies to subsequently opened Recordset objects, and for ADO it applies to the current Recordset.

Note that the Microsoft Jet Provider does not support the OLE DB interfaces that ADO could use to filter and sort the Recordset (IViewFilter and IViewSort). In the case of Filter, ADO will perform the filter itself. However, for Sort you must use the Cursor Service by specifying adUseClient for the CursorLocation property prior to opening the Recordset. The Cursor Service will copy all of the records in the Recordset to a cache on your local machine and will build temporary indexes in order to perform the sorting. In many cases, you may achieve better performance by reexecuting the query used to open the Recordset and specifying an SQL WHERE or ORDER BY clause as appropriate.

Also, you may not get identical results with DAO and ADO when sorting Recordset objects. Different sort algorithms can create different sequences for records that have equal values in the sorted fields. In the previous example, the DAO code gives 'RANCH' as the CustomerId for the first record, while the ADO code gives 'CACTU' as the CustomerId. Both results are valid.

Updating Data in a Recordset

Once you have opened an updatable recordset by specifying the appropriate DAO Recordset object Type or ADO CursorType and LockType, you can change, delete, or add new records using methods of the Recordset object.

Adding New Records

Both DAO and ADO allow you to add new records to an updatable Recordset by first calling the AddNew method, then specifying the values for the fields, and finally committing the changes with the Update method. The following code shows how to add a new record using DAO and ADO.

DAO

Sub DAOAddRecord()

 

   Dim db As DAO.Database

   Dim rst As DAO.Recordset

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Open the Recordset

   Set rst = db.OpenRecordset _

      ("SELECT * FROM Customers", dbOpenDynaset)

 

   ' Add a new record

   rst.AddNew

 

   ' Specify the values for the fields

   rst!CustomerId = "HENRY"

   rst!CompanyName = "Henry's Chop House"

   rst!ContactName = "Mark Henry"

   rst!ContactTitle = "Sales Representative"

   rst!Address = "40178 NE 8th Street"

   rst!City = "Bellevue"

   rst!Region = "WA"

   rst!PostalCode = "98107"

   rst!Country = "USA"

   rst!Phone = "(425) 555-9876"

   rst!Fax = "(425) 555-8908"

 

   ' Save the changes you made to the

   ' current record in the Recordset

   rst.Update

 

   ' For this example, just print out

   ' CustomerId for the new record

   ' Position recordset on new record

   rst.Bookmark = rst.LastModified

   Debug.Print rst!CustomerId

 

   ' Close the recordset

   rst.Close

 

End Sub

ADO

Sub ADOAddRecord()

 

   Dim cnn As New ADODB.Connection

   Dim rst As New ADODB.Recordset

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the recordset

   rst.Open "SELECT * FROM Customers", _

      cnn, adOpenKeyset, adLockOptimistic

 

   ' Add a new record

   rst.AddNew

 

   ' Specify the values for the fields

   rst!CustomerId = "HENRY"

   rst!CompanyName = "Henry's Chop House"

   rst!ContactName = "Mark Henry"

   rst!ContactTitle = "Sales Representative"

   rst!Address = "40178 NE 8th Street"

   rst!City = "Bellevue"

   rst!Region = "WA"

   rst!PostalCode = "98107"

   rst!Country = "USA"

   rst!Phone = "(425) 555-9876"

   rst!Fax = "(425) 555-8908"

 

   ' Save the changes you made to the

   ' current record in the Recordset

   rst.Update

 

   ' For this example, just print out

   ' CustomerId for the new record

   Debug.Print rst!CustomerId

 

   ' Close the recordset

   rst.Close

 

End Sub

DAO and ADO behave differently when a new record is added. With DAO the record that was current before you used AddNew remains current. With ADO the newly inserted record becomes the current record. Because of this, it is not necessary to explicitly reposition on the new record to get information such as the value of an auto-increment column for the new record. For this reason, in the previous ADO example there is no equivalent code to the rst.Bookmark = rst.LastModified code found in the DAO example.

ADO also provides a shortcut syntax for adding new records. The AddNew method has two optional parameters, FieldList and Values, that take an array of field names and field values respectively. The following example demonstrates how to use the shortcut syntax:

Sub ADOAddRecord2()

 

   Dim cnn As New ADODB.Connection

   Dim rst As New ADODB.Recordset

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the recordset

   rst.Open "SELECT * FROM Shippers", _

      cnn, adOpenKeyset, adLockOptimistic

 

   ' Add a new record

   rst.AddNew Array("CompanyName", "Phone"), _

      Array("World Express", "(425) 555-7863")

 

   ' Save the changes you made to the

   ' current record in the Recordset

   rst.Update

 

   ' For this example, just print out the

   ' ShipperId for the new row.

   Debug.Print rst!ShipperId

 

   ' Close the recordset

   rst.Close

 

End Sub

Updating Existing Records

The following code demonstrates how to open a scrollable, updatable Recordset and modify the data in a record.

DAO

Sub DAOUpdateRecord()

 

   Dim db As DAO.Database

   Dim rst As DAO.Recordset

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Open the Recordset

   Set rst = db.OpenRecordset _

      ("SELECT * FROM Customers WHERE CustomerId = 'LAZYK'", _

      dbOpenDynaset)

 

   ' Put the Recordset in Edit Mode

   rst.Edit

 

   ' Update the Contact name of the

   ' first record

   rst.Fields("ContactName").Value = "New Name"

 

   ' Save the changes you made to the

   ' current record in the Recordset

   rst.Update

 

   ' Close the recordset

   rst.Close

 

End Sub

ADO

Sub ADOUpdateRecord()

 

   Dim cnn As New ADODB.Connection

   Dim rst As New ADODB.Recordset

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" _

      & "Data Source=.\NorthWind.mdb;"

 

   ' Open the recordset

   rst.Open _

      "SELECT * FROM Customers WHERE CustomerId = 'LAZYK'", _

      cnn, adOpenKeyset, adLockOptimistic

 

   ' Update the Contact name of the

   ' first record

   rst.Fields("ContactName").Value = "New Name"

 

   ' Save the changes you made to the

   ' current record in the Recordset

   rst.Update

 

   ' Close the recordset

   rst.Close

 

End Sub

Alternatively, in both the DAO and ADO code examples the explicit syntax:

    rst.Fields("ContactName").Value = "New Name"

can be shortened to:

    rst!ContactName = "New Name"

The ADO and DAO code for updating data in a Recordset is very similar. The major difference between the two previous examples is that DAO requires you to put the Recordset into an editable state with the Edit method. ADO does not require you to explicitly indicate that you want to be in edit mode. With both DAO and ADO you can verify the edit status of the current record by using the EditMode property.

One difference between DAO and ADO is the behavior when updating a record and then moving to another record without calling the Update method. With DAO any changes made to the current record are lost when moving to another record without first calling Update. ADO automatically commits the changes to the current record when moving to a new record. You can explicitly discard changes to the current record with both DAO and ADO by using the CancelUpdate method.

Large Text and Binary Data Fields

Two kinds of fields in a Microsoft Jet database are used to store lengthy values. Pictures, OLE objects, or whole files can be stored in an OLE object field as binary data. An OLE object field is also called a Binary Large Object (BLOB) field. A Memo field stores text data only. Memo fields are sometimes called BLOB fields, too, but that is somewhat misleading. A Memo field is merely a variable-length Text field. Both fields are handled similarly.

Large variable-length fields like these must be handled differently than ordinary fixed-length fields, such as integer and date fields. Typically, a variant, a string, or a dynamic byte array is used to read or update the value of the field. However, sometimes the length of the data stored in the field will be too large to store at once in a variant. Breaking the data into smaller, manageable chunks reduces the memory that must be allocated to work with these values. The GetChunk and AppendChunk methods of the Field objects in DAO and ADO break up the data in these fields into smaller chunks.

The following listings demonstrate how to use GetChunk to read large values from a Memo field.

DAO

Sub DAOReadMemo()

 

   Dim db As DAO.Database

   Dim rst As DAO.Recordset

   Dim sNotes As String

   Dim sChunk As String

   Dim cchChunkReceived As Long

   Dim cchChunkRequested As Long

   Dim cchChunkOffset As Long

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Open the Recordset

   Set rst = db.OpenRecordset _

      ("SELECT Notes FROM Employees", dbOpenDynaset)

 

   ' Initialize offset

   cchChunkOffset = 0

 

   ' cchChunkRequested artifically set low at 16

   ' to demonstrate looping

   cchChunkRequested = 16

 

   ' Loop through as many chunks as it takes

   ' to read the entire BLOB into memory

   Do

      ' Temporarily store the next chunk

      sChunk = rst!Fields("Notes").GetChunk _

         (cchChunkOffset, cchChunkRequested)

 

      ' Check how much we got

      cchChunkReceived = Len(sChunk)

 

      ' Adjust offset for next iteration

      cchChunkOffset = cchChunkOffset + cchChunkReceived

 

      ' If we got anything,

      ' concatenate it to the main BLOB

      If cchChunkReceived > 0 Then

         sNotes = sNotes & sChunk

      End If

 

   Loop While cchChunkReceived = cchChunkRequested

 

   ' For this example, print the value of

   ' the Notes field for just the first record

   Debug.Print sNotes

 

   ' Close the recordset

   rst.Close

 

End Sub

ADO

Sub ADOReadMemo()

 

   Dim cnn As New ADODB.Connection

   Dim rst As New ADODB.Recordset

   Dim sNotes As String

   Dim sChunk As String

   Dim cchChunkReceived As Long

   Dim cchChunkRequested As Long

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the recordset

   rst.Open "SELECT Notes FROM Employees ", _

      cnn, adOpenKeyset, adLockOptimistic

 

   ' cchChunkRequested artifically set low at 16

   ' to demonstrate looping

   cchChunkRequested = 16

 

   ' Loop through as many chunks as it takes

   ' to read the entire BLOB into memory

   Do

      ' Temporarily store the next chunk

      sChunk = rst.Fields("Notes").GetChunk(cchChunkRequested)

 

      ' Check how much we got

      cchChunkReceived = Len(sChunk)

 

      ' If we got anything,

      ' concatenate it to the main BLOB

      If cchChunkReceived > 0 Then

         sNotes = sNotes & sChunk

      End If

 

   Loop While cchChunkReceived = cchChunkRequested

 

   ' For this example, print the value of

   ' the Notes field for just the first record

   Debug.Print sNotes

 

   ' Close the recordset

   rst.Close

 

End Sub

The code to use GetChunk and AppendChunk in ADO is similar to the code in DAO. In the DAO example the offset to read the next chunk of data from the field must be explicitly calculated and given as a parameter to the GetChunk method. In ADO the Recordset stores the offset for consecutive GetChunk calls on the same field automatically. Any clones of the Recordset share this offset. This offset is automatically incremented as you call GetChunk consecutively on a single field. Reading from a different field in the Recordset or moving the Recordset to a different record will reset the offset to the beginning of the field.

The following listings demonstrate how to update binary data in an OLE object field without using the GetChunk or AppendChunk methods.

DAO

Sub DAOUpdateBLOB()

 

   Dim db As DAO.Database

   Dim rst As DAO.Recordset

   Dim rgPhoto() As Byte

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Open the Recordset

   Set rst = db.OpenRecordset( _

      "SELECT Photo FROM Employees", dbOpenDynaset)

 

   ' Get the first photo

   rgPhoto = rst.Fields("Photo").Value

 

   ' Move to the next record

   rst.MoveNext

 

   ' Put the Recordset in Edit Mode

   rst.Edit

 

   ' Copy the photo into the next record

   rst.Fields("Photo").Value = rgPhoto

 

   ' Save the changes you made to the

   ' current record in the Recordset

   rst.Update

 

   ' Close the recordset

   rst.Close

 

End Sub

ADO

Sub ADOUpdateBLOB()

 

   Dim cnn As New ADODB.Connection

   Dim rst As New ADODB.Recordset

   Dim rgPhoto() As Byte

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the recordset

   rst.Open "SELECT Photo FROM Employees ", _

      cnn, adOpenKeyset, adLockOptimistic

 

   ' Get the first photo

   rgPhoto = rst.Fields("Photo").Value

 

   ' Move to the next record

   rst.MoveNext

 

   ' Copy the photo into the next record

   rst.Fields("Photo").Value = rgPhoto

 

   ' Save the changes you made to the

   ' current record in the Recordset

   rst.Update

 

   ' Close the recordset

   rst.Close

 

End Sub

The code for updating binary data without using GetChunk or AppendChunk is almost identical to the code for updating ordinary fields such as text, numeric, or date fields. The only difference is that it uses a dynamic byte array to store the values. The previous ADO code shows the same differences from DAO as was shown earlier in the example for updating a record.

Executing Queries

You can execute a query with ADO as easily as you can with DAO. ADO uses a slightly different technique for working with queries, but provides more functionality than DAO provides. ADO also has a number of abbreviated syntaxes that allow you to do the same thing as you would with DAO but with a lot less code.

Executing a Non-Parameterized Stored Query

A non-parameterized stored query is an SQL statement that has been saved in the database and does not require that additional variable information be specified in order to execute. The following listings demonstrate how to execute such a query.

DAO

Sub DAOExecuteQuery()

 

   Dim db As DAO.Database

   Dim rst As DAO.Recordset

   Dim fld As DAO.Field

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Open the Recordset

   Set rst = db.OpenRecordset("Products Above Average Price", _

      dbOpenForwardOnly, dbReadOnly)

 

   ' Display the records in the

   ' debug window

   Do Until rst.EOF

      For Each fld In rst.Fields

         Debug.Print fld.Value & ";";

      Next

      Debug.Print

      rst.MoveNext

   Loop

 

   ' Close the recordset

   rst.Close

 

End Sub

ADO

Sub ADOExecuteQuery()

 

   Dim cnn As New ADODB.Connection

   Dim rst As New ADODB.Recordset

   Dim fld As ADODB.Field

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the recordset

   rst.Open "[Products Above Average Price]", _

      cnn, adOpenForwardOnly, adLockReadOnly, adCmdStoredProc

 

   ' Display the records in the

   ' debug window

   Do Until rst.EOF

      For Each fld In rst.Fields

         Debug.Print fld.Value & ";";

      Next

      Debug.Print

      rst.MoveNext

   Loop

 

   ' Close the recordset

   rst.Close

 

End Sub

The code for executing a non-parameterized, row-returning query is almost identical. With ADO if the query name contains spaces you must use square brackets ([ ]) around the name.

Executing a Parameterized Stored Query

A parameterized stored query is an SQL statement that has been saved in the database and requires that additional variable information be specified in order to execute. The following code shows how to execute such a query.

DAO

Sub DAOExecuteParamQuery()

 

   Dim db As DAO.Database

   Dim qdf As DAO.QueryDef

   Dim rst As DAO.Recordset

   Dim fld As DAO.Field

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Get the QueryDef from the

   ' QueryDefs collection

   Set qdf = db.QueryDefs("Sales by Year")

 

   ' Specify the parameter values

   qdf.Parameters _

      ("Forms!Sales by Year Dialog!BeginningDate") = #8/1/1997#

   qdf.Parameters _

      ("Forms!Sales by Year Dialog!EndingDate") = #8/31/1997#

 

   ' Open the Recordset

   Set rst = qdf.OpenRecordset(dbOpenForwardOnly, dbReadOnly)

 

   ' Display the records in the

   ' debug window

   Do Until rst.EOF

      For Each fld In rst.Fields

         Debug.Print fld.Value & ";";

      Next

      Debug.Print

      rst.MoveNext

   Loop

 

   ' Close the recordset

   rst.Close

 

End Sub

ADO

Sub ADOExecuteParamQuery()

 

   Dim cnn As New ADODB.Connection

   Dim cat As New ADOX.Catalog

   Dim cmd As ADODB.Command

   Dim rst As New ADODB.Recordset

   Dim fld As ADODB.Field

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the catalog

   cat.ActiveConnection = cnn

 

   ' Get the Command object from the

   ' Procedure

   Set cmd = cat.Procedures("Sales by Year").Command

 

   ' Specify the parameter values

   cmd.Parameters _

      ("Forms![Sales by Year Dialog]!BeginningDate") = #8/1/1997#

   cmd.Parameters _

      ("Forms![Sales by Year Dialog]!EndingDate") = #8/31/1997#

 

   ' Open the recordset

   rst.Open cmd, , adOpenForwardOnly, _

      adLockReadOnly, adCmdStoredProc

 

   ' Display the records in the

   ' debug window

   Do Until rst.EOF

      For Each fld In rst.Fields

         Debug.Print fld.Value & ";";

      Next

      Debug.Print

      rst.MoveNext

   Loop

 

   ' Close the recordset

   rst.Close

 

End Sub

Alternatively, the ADO example could be written more concisely by specifying the parameter values using the Parameters parameter with the Command object's Execute method. The following lines of code:

   ' Specify the parameter values

   cmd.Parameters _

      ("Forms![Sales by Year Dialog]!BeginningDate") = #8/1/1997#

   cmd.Parameters _

      ("Forms![Sales by Year Dialog]!EndingDate") = #8/31/1997#

 

   ' Open the recordset

   rst.Open cmd, , adOpenForwardOnly, _

      adLockReadOnly, adCmdStoredProc

could be replaced by the single line:

   ' Execute the Command, passing in the

   ' values for the parameters

   Set rst = cmd.Execute(, Array(#8/1/1997#, #8/31/1997#), _

      adCmdStoredProc)

Note that the Microsoft Jet Provider does not support the Refresh method of the Parameters collection. This means you cannot retrieve the parameters needed for a parameterized query; instead, you must manually populate the Parameters collection.

In one more variation of the ADO code to execute a parameterized query, the example could be rewritten to not use any ADOX code:

Sub ADOExecuteParamQuery2()

 

   Dim cnn As New ADODB.Connection

   Dim cmd As New ADODB.Command

   Dim rst As New ADODB.Recordset

   Dim fld As ADODB.Field

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Create the command

   Set cmd.ActiveConnection = cnn

   cmd.CommandText = "[Sales by Year]"

 

   ' Execute the Command, passing in the

   ' values for the parameters

   Set rst = cmd.Execute(, Array(#8/1/1997#, #8/31/1997#), _

      adCmdStoredProc)

 

   ' Display the records in the

   ' debug window

   Do Until rst.EOF

      For Each fld In rst.Fields

         Debug.Print fld.Value & ";";

      Next

      Debug.Print

      rst.MoveNext

   Loop

 

   ' Close the recordset

   rst.Close

 

End Sub

Executing Bulk Operations

The ADO Command object's Execute method can be used for row-returning queries, as shown in the previous section, as well as for non-row-returning queries—also known as bulk operations. The following code examples demonstrate how to execute a bulk operation in both DAO and ADO.

DAO

Sub DAOExecuteBulkOpQuery()

 

   Dim db As DAO.Database

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Execute the query

   db.Execute "UPDATE Customers SET Country = 'United States' " & _

      "WHERE Country = 'USA'"

 

   Debug.Print "Records Affected = " & db.RecordsAffected

 

   ' Close the database

   db.Close

 

End Sub

ADO

Sub ADOExecuteBulkOpQuery()

 

   Dim cnn As New ADODB.Connection

   Dim iAffected As Integer

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Execute the query

   cnn.Execute "UPDATE Customers SET Country = 'United States' " & _

      "WHERE Country = 'USA'", iAffected, adExecuteNoRecords

 

   Debug.Print "Records Affected = " & iAffected

 

   ' Close the connection

   cnn.Close

 

End Sub

Unlike DAO, which has two methods for executing SQL statements, OpenRecordset and Execute, ADO has a single method, Execute, that executes row-returning as well as bulk operations. In the ADO example the constant adExecuteNoRecords indicates that the SQL statement is non-row returning. If this constant is omitted, the ADO code will still execute successfully, but you will pay a performance penalty. When adExecuteNoRecords is not specified, ADO will create a Recordset object as the return value for the Execute method. Creating this object is unnecessary overhead if the statement does not return records and should be avoided by specifying adExecuteNoRecords when you know that the statement is non-row returning.


Defining and Retrieving a Database's Schema

A database contains a number of tables, indexes, constraints, and so on. Collectively the definition of these items is known as the database's schema. Both DAO and ADO offer an easy, object-based way to both create new objects in the database as well as to retrieve information about the definition of existing objects.

Creating a Database

Before tables or other objects can be defined, the database itself must be created. The following code creates and opens a new Microsoft Jet database.

DAO

Sub DAOCreateDatabase()

 

   Dim db As DAO.Database

 

   Set db = DBEngine.CreateDatabase(".\New.mdb", dbLangGeneral)

 

End Sub

ADOX

Sub ADOCreateDatabase()

 

   Dim cat As New ADOX.Catalog

 

   cat.Create "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\New.mdb;"

 

End Sub

As discussed earlier in this article (see the section "ADOX: Data Definition and Security"), the DAO Database object corresponds to the Catalog object in ADOX. So, to create a new Jet database using ADOX, you use the Catalog objects Create method.

In the preceding DAO code the Locale parameter is specified as dbLangGeneral. In the ADOX code, locale is not explicitly specified. The default locale for the Microsoft Jet Provider is equivalent to dbLangGeneral. Use the ADO Locale Identifier property to specify a different locale.

In DAO CreateDatabase also can take a third Options parameter, specifying information for encryption and database version. For example, the following line is used to create an encrypted, version 1.1 Microsoft Jet database:

   Set db = DBEngine.CreateDatabase(".\New.mdb", dbLangGeneral, _

      dbEncrypt Or dbVersion11)

In ADO encryption and database version information is specified by provider-specific properties. With the Microsoft Jet Provider, use the Encrypt Database and Engine Type properties, respectively. The following line of code specifies these values in the connection string to create an encrypted, version 1.1 Microsoft Jet database:

   cat.Create "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\New.mdb;" & _

      "Jet OLEDB:Encrypt Database=True;" & _

      "Jet OLEDB:Engine Type=2;"

Retrieving Schema Information

Both DAO and ADOX contain collections of objects that can be used to retrieve information about the database's schema. By iterating through the collections, it is easy to determine the structure of the objects in the database.

The following code demonstrates how to print the name of every table in the database by looping through the DAO TableDefs collection and the ADOX Tables collection.

DAO

Sub DAOListTables()

 

   Dim db As DAO.Database

   Dim tbl As DAO.TableDef

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Loop through the tables in the database and print their name

   For Each tbl In db.TableDefs

      Debug.Print tbl.Name

   Next

 

End Sub

ADOX

Sub ADOListTables()

 

   Dim cat As New ADOX.Catalog

   Dim tbl As ADOX.Table

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Loop through the tables in the database and print their name

   For Each tbl In cat.Tables

      If tbl.Type <> "VIEW" Then Debug.Print tbl.Name

   Next

 

End Sub

With DAO the TableDef object represents a table in the database and the TableDefs collection contains a TableDef object for each table in the database. This is similar to ADO, in which the Table object represents a table and the Tables collection contains all the tables.

However, unlike DAO, the ADO Tables collection may contain Table objects that aren't actual tables in your Microsoft Jet database. For example, row-returning, non-parameterized Microsoft Jet queries (considered Views in ADO) are also included in the Tables collection. To determine whether the Table object represents a table in the database, use the Type property. The following table lists the possible values for the Type property when using ADO with the Microsoft Jet Provider.

Type

Description

ACCESS TABLE

The Table is a Microsoft Access system table.

LINK

The Table is a linked table from a non-ODBC data source.

PASS-THROUGH

The Table is a linked table from an ODBC data source.

SYSTEM TABLE

The Table is a Microsoft Jet system table.

TABLE

The Table is a table.

VIEW

The Table is a row-returning, non-parameterized query.

In addition to being able to retrieve schema information using collections in ADOX, you can use the ADO OpenSchema method to return a Recordset containing information about the tables in the database. See "Appendix C: Microsoft Jet 4.0 OLE DB Provider-Defined Property Values" for the schema rowsets globally unique identifiers (GUIDs) that are available in ADO when using the Microsoft Jet Provider.

In general, it is faster to use the OpenSchema method rather than looping through the collection, because ADOX must incur the overhead of creating objects for each element in the collection. The following code demonstrates how to use the OpenSchema method to print the same information as the previous DAO and ADOX examples:

Sub ADOListTables2()

 

   Dim cnn As New ADODB.Connection

   Dim rst As ADODB.Recordset

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the tables schema rowset

   Set rst = cnn.OpenSchema(adSchemaTables)

 

   ' Loop through the results and print

   ' the names in the debug window

   Do Until rst.EOF

      If rst.Fields("TABLE_TYPE") <> "VIEW" Then

         Debug.Print rst.Fields("TABLE_NAME")

      End If

      rst.MoveNext

   Loop

 

End Sub

Creating and Modifying Tables

Microsoft Jet databases can contain two types of tables. The first type is a local table, in which the definition and data are stored within the database. The second type is a linked table, in which the table resides in an external database, but a link along with a copy of the table's definition is stored in the database.

Creating Local Tables

The following example creates a new local table named "Contacts."

DAO

Sub DAOCreateTable()

 

   Dim db As DAO.Database

   Dim tbl As DAO.TableDef

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Create a new TableDef object.

   Set tbl = db.CreateTableDef("Contacts")

 

   With tbl

      ' Create fields and append them to the new TableDef object.

      ' This must be done before appending the TableDef object to

      ' the TableDefs collection of the Database.

      .Fields.Append .CreateField("ContactName", dbText)

      .Fields.Append .CreateField("ContactTitle", dbText)

      .Fields.Append .CreateField("Phone", dbText)

      .Fields.Append .CreateField("Notes", dbMemo)

      .Fields("Notes").Required = False

   End With

 

   ' Add the new table to the database.

   db.TableDefs.Append tbl

 

   db.Close

 

End Sub

ADOX

Sub ADOCreateTable()

 

   Dim cat As New ADOX.Catalog

   Dim tbl As New ADOX.Table

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Create a new Table object.

   With tbl

      .Name = "Contacts"

      ' Create fields and append them to the new Table

      ' object. This must be done before appending the

      ' Table object to the Tables collection of the

      ' Catalog.

      .Columns.Append "ContactName", adVarWChar

      .Columns.Append "ContactTitle", adVarWChar

      .Columns.Append "Phone", adVarWChar

      .Columns.Append "Notes", adLongVarWChar

      .Columns("Notes").Attributes = adColNullable

   End With

 

   ' Add the new table to the database.

   cat.Tables.Append tbl

 

   Set cat = Nothing

 

End Sub

The process for creating a table using DAO or ADOX is the same. First, create the object (TableDef or Table), append the columns (Field or Column objects) and, finally, append the table to the collection. Though the process is the same, the syntax is slightly different.

With ADOX it is not necessary to use a "create" method to create the column before appending it to the collection. The Append method can be used to both create and append the column.

You'll also notice the data type names for the columns are different between DAO and ADOX. The following table shows how the DAO data types that apply to Microsoft Jet databases map to the ADO data types.

DAO data type

ADO data type

dbBinary

adBinary

dbBoolean

adBoolean

dbByte

adUnsignedTinyInt

dbCurrency

adCurrency

dbDate

adDate

dbDecimal

adNumeric

dbDouble

adDouble

dbGUID

adGUID

dbInteger

adSmallInt

dbLong

adInteger

dbLongBinary

adLongVarBinary

dbMemo

adLongVarWChar

dbSingle

adSingle

dbText

adVarWChar

Though not shown in this example, there are a number of other attributes of a table or column that you can set when creating the table or column, using the DAO Attributes property. The following table shows how these attributes map to ADO and Microsoft Jet Provider–specific properties.

DAO TableDef
property

Value

ADOX Table
property

Value

Attributes

dbAttachExclusive

Jet OLEDB:Exclusive Link

True

Attributes

dbAttachSavePWD

Jet OLEDB:Cache Link Name/Password

True

Attributes

dbAttachedTable

Type

"LINK"

Attributes

dbAttachedODBC

Type

"PASS-THROUGH"

 

DAO Field
property

Value

ADOX Column
property

Value

Attributes

dbAutoIncrField

AutoIncrement

True

Attributes

dbFixedField

ColumnAttributes

adColFixed

Attributes

dbHyperlinkField

Jet OLEDB:Hyperlink

True

Attributes

dbSystemField

No equivalent

n/a

Attributes

dbUpdatableField

Attributes (Field Object)

adFldUpdatable

Attributes

dbVariableField

ColumnAttributes

Not adColFixed

Creating a Linked Table

Linking (also known as attaching) a table from an external database allows you to read data, update and add data (in most cases), and create queries using the table in the same way as you would with a table native to the database.

With Microsoft Jet you can create links to Microsoft Jet data, ISAM data (Text, FoxPro, dBASE, and so on), and ODBC data. Tables that are linked through ODBC are sometimes called pass-through tables.

The following listings demonstrate how to create a table that is linked to a table in another Microsoft Jet database.

DAO

Sub DAOCreateAttachedJetTable()

 

   Dim db As DAO.Database

   Dim tbl As DAO.TableDef

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Create a new TableDef object.

   Set tbl = db.CreateTableDef("Authors")

 

   ' Set the properties to create the link

   tbl.Connect = ";DATABASE=.\Pubs.mdb;pwd=password;"

   tbl.SourceTableName = "authors"

 

   ' Add the new table to the database.

   db.TableDefs.Append tbl

 

   db.Close

 

End Sub

ADOX

Sub ADOCreateAttachedJetTable()

 

   Dim cat As New ADOX.Catalog

   Dim tbl As New ADOX.Table

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Set the name and target catalog for the table

   tbl.Name = "Authors"

   Set tbl.ParentCatalog = cat

 

   ' Set the properties to create the link

   tbl.Properties("Jet OLEDB:Create Link") = True

   tbl.Properties("Jet OLEDB:Link Datasource") = ".\Pubs.mdb"

   tbl.Properties("Jet OLEDB:Link Provider String") = ";Pwd=password"

   tbl.Properties("Jet OLEDB:Remote Table Name") = "authors"

 

   ' Append the table to the collection

   cat.Tables.Append tbl

 

   Set cat = Nothing

 

End Sub

To create a linked table you must specify the external data source and the name of the external table. With DAO the Connect and SourceTableName properties are used to specify this information. With ADOX several Microsoft Jet Provider-specific properties are used to create the link. When referencing the Table object's Properties collection prior to appending the Table to the Tables collection, you must first set the ParentCatalog property. This is necessary so ADOX knows from which OLE DB Provider to receive the property information. See the section "Appendix B: Microsoft Jet 4.0 OLE DB Properties Reference" for more information about the properties that are available in the Table object's Properties collection when using the Microsoft Jet Provider.

With ADOX the Jet OLEDB:Link Datasource property contains only the file and path name for the database. It does not contain the "database=;" prefix nor is it used to specify the database password or other connection options as the Connect property does in DAO. To specify other connection options in ADOX code, use the Jet OLEDB:Link Provider String property. You do not need to set this property unless you need to set extra connection options. In the previous example, if the pubs.mdb was not secured with a database password you could omit the line of code that sets the Jet OLEDB:Link Provider String property.

When used for a user ID or a password, the value for this property follows the syntax for connection strings used for external data. The syntax is given in the Microsoft Jet Database Engine Programmer's Guide. Specifically, you must use "uid=;" and "pwd=;" to set the user ID and password, respectively, and not "User ID=;" or "Password=;".

Notice that when creating an attached table using both DAO and ADOX it is not necessary to create columns on the table. The Microsoft Jet database engine will automatically create the columns based on the definition of the table in the external data source.

This next example shows how to create a table that is linked to a table in an ODBC data source such as a Microsoft SQL Server database.

DAO

Sub DAOCreateAttachedODBCTable()

 

   Dim db As DAO.Database

   Dim tbl As DAO.TableDef

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Create a new TableDef object.

   Set tbl = db.CreateTableDef("Titles")

 

   ' Set the properties to create the link

   tbl.Connect = "ODBC;DSN=ADOPubs;UID=sa;PWD=;"

   tbl.SourceTableName = "titles"

 

   ' Add the new table to the database.

   db.TableDefs.Append tbl

 

   db.Close

 

End Sub

ADOX

Sub ADOCreateAttachedODBCTable()

 

   Dim cat As New ADOX.Catalog

   Dim tbl As New ADOX.Table

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Set the name and target catalog for the table

   tbl.Name = "Titles"

   Set tbl.ParentCatalog = cat

 

   ' Set the properties to create the link

   tbl.Properties("Jet OLEDB:Create Link") = True

   tbl.Properties("Jet OLEDB:Link Provider String") = _

      "ODBC;DSN=ADOPubs;UID=sa;PWD=;"

   tbl.Properties("Jet OLEDB:Remote Table Name") = "titles"

 

   ' Append the table to the collection

   cat.Tables.Append tbl

 

   Set cat = Nothing

 

End Sub

Unlike DAO, which has a single Connect property, ADOX with the Microsoft Jet Provider has a separate property that specifies the connection string for tables attached through ODBC. When creating tables attached through ODBC you may want to indicate that the password should be saved as part of the connection string (it is not saved by default). With ADOX, use the Jet OLEDB:Cache Link Name/Password property to indicate that the password should be cached. This is equivalent to setting the dbAttachSavePWD flag in the Table object's Attributes property using DAO.

Modifying an Existing Table

Once a table is created, you may want to modify it to add or remove columns, change the validation rule, or refresh the link for a linked table.

The following listings demonstrate how to add a new auto-increment column to an existing table.

DAO

Sub DAOCreateAutoIncrColumn()

 

   Dim db As DAO.Database

   Dim tbl As DAO.TableDef

   Dim fld As DAO.Field

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Get the Contacts table

   Set tbl = db.TableDefs("Contacts")

 

   ' Create the new auto increment column

   Set fld = tbl.CreateField("ContactId", dbLong)

   fld.Attributes = dbAutoIncrField

 

   ' Add the new table to the database.

   tbl.Fields.Append fld

 

   db.Close

 

End Sub

ADOX

Sub ADOCreateAutoIncrColumn()

 

   Dim cat As New ADOX.Catalog

   Dim col As New ADOX.Column

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Create the new auto increment column

   With col

      .Name = "ContactId"

      .Type = adInteger

      Set .ParentCatalog = cat

      .Properties("AutoIncrement") = True

   End With

 

   ' Append the column to the table

   cat.Tables("Contacts").Columns.Append col

 

   Set cat = Nothing

 

End Sub

In the ADOX example, notice that the ParentCatalog property of the Column object is set before the AutoIncrement property in the Properties collection is set to True. In order to access any property in the Properties collection, the Column object must be associated with a provider.

The next example shows how to update an existing linked table to refresh the link. This involves updating the connection string for the table and then resetting the Jet OLEDB:CreateLink property to tell Microsoft Jet to reestablish the link:

DAO

Sub DAORefreshLinks()

 

   Dim db As DAO.Database

   Dim tbl As DAO.TableDef

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   For Each tbl In db.TableDefs

      ' Check to make sure table is a linked table.

      If (tbl.Attributes And dbAttachedTable) Then

         tbl.Connect = "MS Access;PWD=NewPassWord;" & _

            "DATABASE=.\NewPubs.mdb"

         tbl.RefreshLink

      End If

   Next

 

End Sub

ADOX

Sub ADORefreshLinks()

 

   Dim cat As New ADOX.Catalog

   Dim tbl As ADOX.Table

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   For Each tbl In cat.Tables

      ' Check to make sure table is a linked table.

      If tbl.Type = "LINK" Then

         tbl.Properties("Jet OLEDB:Create Link") = False

         tbl.Properties("Jet OLEDB:Link Provider String") = _

            ";pwd=NewPassWord"

         tbl.Properties("Jet OLEDB:Link Datasource") = _

            ".\NewPubs.mdb"

         tbl.Properties("Jet OLEDB:Create Link") = True

      End If

   Next

 

End Sub

Creating an Index

Indexes on a column or columns in a table specify the order of records accessed from database tables and whether duplicate records are accepted. The following code creates an index on the Country field of the Employees table.

DAO

Sub DAOCreateIndex()

 

   Dim db As DAO.Database

   Dim tbl As DAO.TableDef

   Dim idx As DAO.Index

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   Set tbl = db.TableDefs("Employees")

 

   ' Create Index object append Field object to the Index object.

   Set idx = tbl.CreateIndex("CountryIndex")

   idx.Fields.Append idx.CreateField("Country")

 

   ' Append the Index object to the

   ' Indexes collection of the TableDef.

   tbl.Indexes.Append idx

 

   db.Close

 

End Sub

ADOX

Sub ADOCreateIndex()

 

   Dim cat As New ADOX.Catalog

   Dim tbl As ADOX.Table

   Dim idx As New ADOX.Index

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   Set tbl = cat.Tables("Employees")

 

   ' Create Index object append table columns to it.

   idx.Name = "CountryIndex"

   idx.Columns.Append "Country"

 

   ' Allow Null values to be added in the index field

   idx.IndexNulls = adIndexNullsAllow

 

   ' Append the Index object to the Indexes collection of Table

   tbl.Indexes.Append idx

 

   Set cat = Nothing

 

End Sub

The process for creating an index is the same in ADO and DAO: Create the index, append columns to the index, and then append the index to the table. However, there are some differences in behavior between the Index objects in these two models. DAO has two properties, Required and IgnoreNulls, that together determine whether or not Null values can be inserted for fields in the index and whether index entries will be created when some of the fields in a multicolumn index contain Null. By default, both of these properties are False, indicating that Null values are allowed in the index and that an index entry will be added. This differs from ADO, which has a single property, IndexNulls, for this purpose. By default, the IndexNulls property is adIndexNullsDisallow, which indicates that Null values are not allowed in the index and that no index entry will be added if a field in the index contains Null.

The following table shows the mapping between the DAO Required and IgnoreNulls properties to the ADOX IndexNulls property.

DAO
required

DAO
IgnoreNulls

ADOX
IndexNulls

Description

True

False

adIndexNullsDisallow

A Null value isn't allowed in the index field; no index entry added.

False

True

adIndexNullsIgnore

A Null value is allowed in the index field; no index entry added.

False

False

adIndexNullsAllow

A Null value is allowed in the index field; index entry added.

Note that ADO defines an additional value for the IndexNulls property, adIndexNullsIgnoreAny, that is not listed in the preceding table. The Microsoft Jet Provider does not support this type of index. Setting IgnoreNulls to adIndexNullsIgnoreAny when using the Microsoft Jet Provider will result in a run-time error. The purpose of adIndexNullsIgnoreAny, if it was to be supported by a provider, is to ignore an entry if any column of a multicolumn index contains a Null value.

Defining Keys and Relationships

Once the structure of a table has been defined, it is useful to define keys for the table and the relationships between tables. Microsoft Jet will use the information provided in the key and relationship definitions to optimize queries.

Creating a Primary Key

A table often has a column or combination of columns whose values uniquely identify a row in a table. This column (or combination of columns) is called the primary key of the table. When you define a primary key, the Microsoft Jet database engine will create an index to enforce the uniqueness of the key.

Using the Contacts table created in previous examples, the following listings demonstrate how to make the ContactId column the primary key.

DAO

Sub DAOCreatePrimaryKey()

 

   Dim db As DAO.Database

   Dim tbl As DAO.TableDef

   Dim idx As DAO.Index

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   Set tbl = db.TableDefs("Contacts")

 

   ' Create the Primary Key and append table columns to it.

   Set idx = tbl.CreateIndex("PrimaryKey")

   idx.Primary = True

   idx.Fields.Append idx.CreateField("ContactId")

 

   ' Append the Index object to the

   ' Indexes collection of the TableDef.

   tbl.Indexes.Append idx

 

   db.Close

 

End Sub

ADOX

Sub ADOCreatePrimaryKey()

 

   Dim cat As New ADOX.Catalog

   Dim tbl As ADOX.Table

   Dim pk As New ADOX.Key

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   Set tbl = cat.Tables("Contacts")

 

   ' Create the Primary Key and append table columns to it.

   pk.Name = "PrimaryKey"

   pk.Type = adKeyPrimary

   pk.Columns.Append "ContactId"

 

   ' Append the Key object to the Keys collection of Table

   tbl.Keys.Append pk

 

   Set cat = Nothing

 

End Sub

With DAO the Index object is used to create primary keys. The key is created much like any other index except that the Primary property is set to True. ADO, however, has a Key object that is used to create new keys. The steps in creating a key are similar to creating an index. However, when creating a Key you must specify the type of Key you want to create. In this case, the key type is adKeyPrimary, which indicates that you want to create a primary key.

Alternatively, the ADOX code to create and append the key could have been written in a single line of code. The following code:

   ' Create the Primary Key and append table columns to it.

   pk.Name = "PrimaryKey"

   pk.Type = adKeyPrimary

   pk.Columns.Append "ContactId"

 

   ' Append the Key object to the Keys collection of Table

   tbl.Keys.Append pk

is equivalent to:

   ' Append the Key object to the Keys collection of Table

   tbl.Keys.Append "PrimaryKey", adKeyPrimary, "ContactId"

Creating One-to-Many Relationships (Foreign Keys)

One-to-many relationships between tables (where the primary key value in the primary table may appear in multiple rows in the foreign table) are established by creating foreign keys. A foreign key is a column or combination of columns whose values match the primary key of another table. Unlike a primary key, a foreign key does not have to be unique.

DAO

Sub DAOCreateForeignKey()

 

   Dim db As DAO.Database

   Dim rel As DAO.Relation

   Dim fld As DAO.Field

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' This key already exists in the Northwind database.

   ' For the purposes of this example, we're going to

   ' delete it and then recreate it

   db.Relations.Delete "CategoriesProducts"

 

   ' Create the relation

   Set rel = db.CreateRelation()

   rel.Name = "CategoriesProducts"

   rel.Table = "Categories"

   rel.ForeignTable = "Products"

 

   ' Create the field the tables are related on

   Set fld = rel.CreateField("CategoryId")

 

   ' Set ForeignName property of the field to the name of

   ' the corresponding field in the primary table

   fld.ForeignName = "CategoryId"

 

   rel.Fields.Append fld

 

   ' Append the relation to the collection

   db.Relations.Append rel

 

End Sub

ADOX

Sub ADOCreateForeignKey()

 

   Dim cat As New ADOX.Catalog

   Dim tbl As ADOX.Table

   Dim fk As New ADOX.Key

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Get the table for the foreign side of the relationship

   Set tbl = cat.Tables("Products")

 

   ' This key already exists in the Northwind database.

   ' For the purposes of this example, we're going to

   ' delete it and then recreate it

   tbl.Keys.Delete "CategoriesProducts"

 

   ' Create the Foreign Key

   fk.Name = "CategoriesProducts"

   fk.Type = adKeyForeign

   fk.RelatedTable = "Categories"

 

   ' Append column(s) in the foreign table to it

   fk.Columns.Append "CategoryId"

 

   ' Set RelatedColumn property to the name of the corresponding

   ' column in the primary table

   fk.Columns("CategoryId").RelatedColumn = "CategoryId"

 

   ' Append the Key object to the Keys collection of Table

   tbl.Keys.Append fk

 

   Set cat = Nothing

 

End Sub

Alternatively, the ADOX code to create and append the key could have been written in a single line of code. The following code:

   ' Create the Foreign Key

   fk.Name = "CategoriesProducts"

   fk.Type = adKeyForeign

   fk.RelatedTable = "Categories"

 

   ' Append column(s) in the foreign table to it

   fk.Columns.Append "CategoryId"

 

   ' Set RelatedColumn property to the name of the corresponding

   ' column in the primary table

   fk.Columns("CategoryId").RelatedColumn = "CategoryId"

 

   ' Append the Key object to the Keys collection of Table

   tbl.Keys.Append fk

is equivalent to:

   ' Append the Key object to the Keys collection of Table

   tbl.Keys.Append "CategoriesProducts", adKeyForeign, _

      "CategoryId", "Categories", "CategoryId"

Enforcing Referential Integrity

Referential integrity preserves the defined relationships between tables when records are added, updated, or deleted. Maintaining referential integrity within your database requires that there be no references to nonexistent values, and that if a key value changes, all references to it change consistently throughout the database.

When you enforce referential integrity users are prevented from adding new records to a related table when there is no associated record in the primary table, changing primary key values that would result in "orphaned" records in the related table, or deleting records in the primary table when there are associated records in the related table.

By default, Microsoft Jet enforces relationships created by DAO or ADOX. A trappable error will occur if you make changes that violate referential integrity. When defining a new relationship, you can also specify that Microsoft Jet should cascade updates or deletes. With cascading updates, when a change is made to the primary key in a record in the primary table, Microsoft Jet will automatically update the foreign key in all related records in the related foreign table or tables. Similarly, with cascading deletes, when a record is deleted from the primary table, Microsoft Jet will automatically delete all related records in the related foreign table or tables.

In the following example, the code from the preceding section is modified to create a foreign key that supports cascading updates and deletes.

DAO

Sub DAOCreateForeignKeyCascade()

 

   Dim db As DAO.Database

   Dim rel As DAO.Relation

   Dim fld As DAO.Field

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' This key already exists in the Northwind database.

   ' For the purposes of this example, we're going to

   ' delete it and then recreate it

   db.Relations.Delete "CategoriesProducts"

 

   ' Create the relation

   Set rel = db.CreateRelation()

   rel.Name = "CategoriesProducts"

   rel.Table = "Categories"

   rel.ForeignTable = "Products"

 

   ' Specify cascading updates and deletes

   rel.Attributes = dbRelationUpdateCascade Or _

      dbRelationDeleteCascade

 

   ' Create the field the tables are related on

   Set fld = rel.CreateField("CategoryId")

   ' Set ForeignName property of the field to the name of

   ' the corresponding field in the primary table

   fld.ForeignName = "CategoryId"

 

   rel.Fields.Append fld

 

   ' Append the relation to the collection

   db.Relations.Append rel

 

End Sub

ADOX

Sub ADOCreateForeignKeyCascade()

 

   Dim cat As New ADOX.Catalog

   Dim tbl As ADOX.Table

   Dim fk As New ADOX.Key

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

   "Data Source=.\NorthWind.mdb;"

 

   ' Get the table for the foreign side of the relationship

   Set tbl = cat.Tables("Products")

 

   ' This key already exists in the Northwind database.

   ' For the purposes of this example, we're going to

   ' delete it and then recreate it

   tbl.Keys.Delete "CategoriesProducts"

 

   ' Create the Foreign Key

   fk.Name = "CategoriesProducts"

   fk.Type = adKeyForeign

   fk.RelatedTable = "Categories"

 

   ' Specify cascading updates and deletes

   fk.UpdateRule = adRICascade

   fk.DeleteRule = adRICascade

 

   ' Append column(s) in the foreign table to it

   fk.Columns.Append "CategoryId"

   ' Set RelatedColumn property to the name of the corresponding

   ' column in the primary table

   fk.Columns("CategoryId").RelatedColumn = "CategoryId"

 

   ' Append the Key object to the Keys collection of Table

   tbl.Keys.Append fk

 

   Set cat = Nothing

 

End Sub

The following table shows how the values for the DAO Attributes property of a Relation object map to properties of the ADOX Key object.

Note The following values for the DAO Attributes property of a Relation object have no corresponding properties in ADOX: dbRelationDontEnforce, dbRelationInherited, dbRelationLeft, and dbRelationRight.

DAO Relation
Object property

Value

ADOX Key
Object property

Value

Attributes

dbRelationUnique

Type

adKeyUnique

Attributes

dbRelationUpdateCascade

UpdateRule

adRICascade

Attributes

dbRelationDeleteCascade

DeleteRule

adRICascade

Creating and Modifying Queries

As discussed in the section "Executing Queries," the ADO Command object is similar to the DAO QueryDef object in that it specifies an SQL string and parameters and executes the query. However, unlike the DAO QueryDef object, the ADO Command object cannot be used directly to persist a query. By specifying a name for the QueryDef when it is created, the DAO QueryDef is automatically appended to the QueryDefs collection and persisted in the database. This differs from ADO, in which all Command objects are temporary queries. You must explicitly append the Command to the ADOX Procedures or Views collection in order to persist it in the database.

The Microsoft Jet Provider defines Microsoft Jet queries as Views if the query is a row-returning, non-parameterized query. The provider defines a procedure as either a non-row-returning query (a bulk operation) or a parameterized row-returning query.

Creating a Stored Query

The following listings demonstrate how to create a row-returning, non-parameterized query.

DAO

Sub DAOCreateQuery()

 

   Dim db As DAO.Database

   Dim qry As DAO.QueryDef

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Create query

   Set qry = db.CreateQueryDef("AllCategories", _

      "SELECT * FROM Categories")

 

   db.Close

 

End Sub

ADOX

Sub ADOCreateQuery()

 

   Dim cat As New ADOX.Catalog

   Dim cmd As New ADODB.Command

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Create the query

   cmd.CommandText = "SELECT * FROM Categories"

   cat.Views.Append "AllCategories", cmd

 

   Set cat = Nothing

 

End Sub

In this example, because the SQL statement is a non-parameterized, row-returning query, the ADO Command object is appended to the ADOX Views collection. Note that when using the Microsoft Jet Provider it is possible to append a Command object to either the Views or Procedures collection regardless of the type of query that is being created. However, if a query such as the one in this example is appended to the Procedures collection, and then the Procedures and Views collections are refreshed, you'll notice that the query is no longer in the Procedures collection, but is now in the Views collection.

Likewise, you can append a parameterized query, or a non-row-returning bulk operation query to either the Views or Procedures collection. However, ADOX will actually store these types of queries in the Procedures collection. If you append to the Views collection, then refresh both the Views and Procedures collections, you'll find that the newly appended query is now in the Procedures collection.

Creating a Parameterized Stored Query

The following listings demonstrate how to create a parameterized query and save it in the database.

DAO

Sub DAOCreateParameterizedQuery()

 

   Dim db As DAO.Database

   Dim qry As DAO.QueryDef

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Create query

   Set qry = db.CreateQueryDef("Employees by Region", _

      "PARAMETERS [prmRegion] TEXT(255);" & _

      "SELECT * FROM Employees WHERE Region = [prmRegion]")

 

   db.Close

 

End Sub

ADOX

Sub ADOCreateParameterizedQuery()

 

   Dim cat As New ADOX.Catalog

   Dim cmd As New ADODB.Command

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Create the Command

   cmd.CommandText = "PARAMETERS [prmRegion] TEXT(255);" & _

      "SELECT * FROM Employees WHERE Region = [prmRegion]"

 

   ' Create the Procedure

   cat.Procedures.Append "Employees by Region", cmd

 

   Set cat = Nothing

 

End Sub

The code for creating a parameterized query is very similar using DAO and ADOX. Note, however, that although the ADO Command object allows you to create parameters using the CreateParameter method, this information will not be saved when creating or updating a Procedure. You must specify the parameters as part of the SQL string.

Also note that Microsoft Jet will interpret the SQL statement differently when a query is created with ADOX and the Microsoft Jet Provider rather than DAO. The Microsoft Jet Provider always sets a Microsoft Jet database engine option for American National Standards Institute (ANSI) compliance. This may cause differences in behavior between DAO and ADO when creating or executing queries. For example, if the SQL statement in the previous code had been written as follows:

   "PARAMETERS [prmRegion] TEXT;" & _

      "SELECT * FROM Employees WHERE Region = [prmRegion]"

                           

omitting the (255) after the Text keyword, the parameter would be created as a Text field (dbText, adVarWChar) when using DAO, but as a Memo field (dbMemo, adLongVarWChar) when using ADO.

Further, some SQL statements that execute when using DAO will fail to execute when using ADO due to additional reserved words. For a list of reserved words, see "Appendix D: Microsoft Jet 4.0 ANSI Reserved Words."

Modifying a Stored Query

The following listings demonstrate how to modify an existing query.

DAO

Sub DAOModifyQuery()

 

   Dim db As DAO.Database

   Dim qry As DAO.QueryDef

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Get the query

   Set qry = db.QueryDefs("Employees by Region")

 

   ' Update the SQL and save the updated query

   qry.SQL = "PARAMETERS [prmRegion] TEXT(255);" & _

      "SELECT * FROM Employees WHERE Region = [prmRegion] " & _

      "ORDER BY City"

 

   db.Close

 

End Sub

ADO

Sub ADOModifyQuery()

 

   Dim cat As New ADOX.Catalog

   Dim cmd As ADODB.Command

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Get the query

   Set cmd = cat.Procedures("Employees by Region").Command

 

   ' Update the SQL

   cmd.CommandText = "PARAMETERS [prmRegion] TEXT(255);" & _

      "SELECT * FROM Employees WHERE Region = [prmRegion] " & _

      "ORDER BY City"

 

   ' Save the updated query

   Set cat.Procedures("Employees by Region").Command = cmd

 

   Set cat = Nothing

 

End Sub

In the ADO code setting the Procedure object's Command property to the modified Command object saves the changes. If this last step were not included, the changes would not have been persisted to the database. This difference results from the fact that ADO Command objects are designed as temporary queries, while DAO QueryDef objects are designed as saved queries. You need to be aware of this when working with Commands, Procedures, and Views. You may think that the following ADO code examples are equivalent:

   Set cmd = cat.Procedures("Employees by Region").Command

   cmd.CommandText = "PARAMETERS [prmRegion] TEXT(255);" & _

      "SELECT * FROM Employees WHERE Region = [prmRegion] " & _

      "ORDER BY City"

   Set cat.Procedures("Employees by Region").Command = cmd

                                                         

and:

   cat.Procedures("Employees by Region").CommandText = _

      "PARAMETERS [prmRegion] TEXT;" & _

      "SELECT * FROM Employees WHERE Region = [prmRegion] " & _

      "ORDER BY City"

                                

However, they are not. Both will compile, but the second piece of code will not actually update the query in the database. In the second example ADOX will create a tear-off command object and hand it back to Visual Basic for Applications. Visual Basic for Applications will then ask ADOX to update the CommandText property, which it does. Finally, Visual Basic for Applications moves to execute the next line of code and the Command object is lost. ADOX is never asked to update the Procedure with the changes to the modified Command object.

Creating an SQL Pass-Through Query

SQL pass-through queries are SQL statements that are sent directly to the database server without interpretation by the Microsoft Jet database engine. When creating an SQL pass-through query, you must specify the SQL statement to execute as well as an ODBC connection string.

With DAO pass-through queries provide a means of improving performance when accessing external ODBC data. With ADO it is not necessary to create SQL pass-through queries in your Microsoft Jet database in order to have good performance when accessing external data. With ADO you can use the Microsoft OLE DB Provider for SQL Server to directly access SQL Server without the overhead of Microsoft Jet or ODBC. You can also use the Microsoft OLE DB Provider for ODBC to access data in any ODBC data source.

While it is no longer necessary to create SQL pass-through queries in your Microsoft Jet database, it is still possible to do so using ADOX and the Microsoft Jet Provider. The following code demonstrates how to create an SQL pass-through query.

DAO

Sub DAOCreateSQLPassThrough()

 

   Dim db As DAO.Database

   Dim qry As DAO.QueryDef

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Create query

   Set qry = db.CreateQueryDef("Business Books", _

      "SELECT * FROM Titles WHERE Type = 'business'")

 

   qry.Connect = "ODBC;DSN=ADOPubs;UID=sa;PWD=;"

   qry.ReturnsRecords = True

 

   db.Close

 

End Sub

ADOX

Sub ADOCreateSQLPassThrough()

 

   Dim cat As New ADOX.Catalog

   Dim cmd As New ADODB.Command

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Create the Command

   Set cmd.ActiveConnection = cat.ActiveConnection

   cmd.CommandText = "SELECT * FROM Titles WHERE Type = 'business'"

   cmd.Properties("Jet OLEDB:ODBC Pass-Through Statement") = True

   cmd.Properties("Jet OLEDB:Pass Through Query Connect String") = _

      "ODBC;DSN=ADOPubs;database=pubs;UID=sa;PWD=;"

 

   ' Create the Procedure

   cat.Procedures.Append "Business Books", cmd

 

   Set cat = Nothing

 

End Sub

 


Security

Microsoft Jet databases can be secured in one of two ways: share-level security or user-level security. For share-level security, the database is secured with a password. Anyone attempting to open the database must specify the correct database password. For user-level security, each user is given a user name and password to open the database.

Changing a Password

The first step in securing a Microsoft Jet database is to change the password for the Admin user, if using user-level security, or changing the database password if using share-level security. When changing a password for a user or database, you must supply both the existing and new passwords. When changing the database or Admin user's password for the first time, use an empty string ("") as the existing password.

The following code shows how to enable user-level security by setting the password for the Admin user to "password."

DAO

Sub DAOChangePassword()

 

   Dim wks As Workspace

   Dim usr As DAO.User

 

   ' Open the workspace, specifying the system database to use

 

   DBEngine.SystemDB = _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   Set wks = DBEngine.CreateWorkspace("", "Admin", "")

 

   ' Change the password for the user Admin

   wks.Users("Admin").NewPassword "", "password"

 

End Sub

ADOX

Sub ADOChangePassword()

 

   Dim cat As New ADOX.Catalog

 

   ' Open the catalog, specifying the system database to use

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;Jet OLEDB:System database=" & _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

 

   ' Change the password for the user Admin

   cat.Users("Admin").ChangePassword "", "password"

 

End Sub

DAO and ADOX both have a method on the User object to change the user's password. The method takes the user's current password and the new password as parameters. In DAO this method is called NewPassword, while in ADOX it is called ChangePassword.

Note The Microsoft Jet Provider will not error on the line of code that opens the catalog if the system database specified is incorrect. However, it will error when attempting to change the password or perform any other security-related operations with the following error if the system database was not correctly specified: "The operation requested by the application is not supported by the provider."

The following code shows how to change the database password for enabling security at the share level.

DAO

Sub DAOChangeDatabasePassword()

 

   ' Make sure there isn't already a file with the

   ' name of the compacted database.

   If Dir(".\NewNorthWind.mdb") <> "" Then _

      Kill ".\NewNorthWind.mdb"

 

   ' Basic compact - creating new database named newnwind

   DBEngine.CompactDatabase ".\NorthWind.mdb", _

      ".\NewNorthWind.mdb", , , ";pwd=password;"

 

   ' Delete the original database

   Kill ".\NorthWind.mdb"

 

   ' Rename the file back to the original name

   Name ".\NewNorthWind.mdb" As ".\NorthWind.mdb"

 

End Sub

JRO

Sub JROChangeDatabasePassword()

 

   Dim je As New JRO.JetEngine

 

   ' Make sure there isn't already a file with the

   ' name of the compacted database.

   If Dir(".\NewNorthWind.mdb") <> "" Then _

      Kill ".\NewNorthWind.mdb"

 

   ' Compact the database specifying the new database password

   je.CompactDatabase "Data Source=.\NorthWind.mdb;", _

      "Data Source=.\NewNorthWind.mdb;" & _

      "Jet OLEDB:Database Password=password"

 

   ' Delete the original database

   Kill ".\NorthWind.mdb"

 

   ' Rename the file back to the original name

   Name ".\NewNorthWind.mdb" As ".\NorthWind.mdb"

 

End Sub

Note JRO, not ADOX, is used to change a database password at share level.

Both DAO and JRO allow you to change the database password when compacting the database. The syntax is slightly different: In DAO specify ";pwd=password;" in the Password parameter of CompactDatabase. In JRO specify the provider-specific "Jet OLEDB:Database Password=password" in the destination connection parameter of CompactDatabase.

Alternatively, the DAO code could be rewritten to use the NewPassword method of the Database object:

Sub DAOChangeDatabasePassword2()

 

   Dim db As DAO.Database

 

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb", True)

   db.NewPassword "", "password"

   db.Close

 

End Sub

A similar mechanism is not currently available in JRO or ADOX. You must use the CompactDatabase method in order to change the database password.

Creating Users and Groups

A User object represents a user account that has specific access permissions, while a Group object represents a group of user accounts that have common access permissions. Creating users and groups allows you to easily control and maintain users' access to the database and objects within the database.

The following code example shows how to create a new user.

DAO

Sub DAOCreateUser()

 

   Dim wks As DAO.Workspace

 

   ' Open a workspace

   DBEngine.SystemDB = _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   Set wks = DBEngine.CreateWorkspace("", "Admin", "password")

 

   ' Create the user and append it to the Users collection

   wks.Users.Append wks.CreateUser("MyUser", "xNewUser", "password")

 

End Sub

ADOX

Sub ADOCreateUser()

 

   Dim cat As New ADOX.Catalog

 

   ' Open the catalog, specifying the system database to use

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;Jet OLEDB:System database=" & _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW;" & _

      "User Id=Admin;Password=password;"

 

   ' Create the new user and append it to the users collection

   cat.Users.Append "MyUser", "password"

 

End Sub

Unlike with DAO, with ADOX you do not have to create a User object before adding the user to the database with the Append method. With ADOX you can create a new user simply by passing the name and password to the Append method of the Users collection.

Setting PID Values

When creating a user or group in DAO you must provide a value for the PID parameter or property. This parameter is not required when creating a new user or group in ADOX because the Microsoft Jet Provider automatically generates PID values. This PID value is not retrievable using ADOX or DAO.

This presents problems if the workgroup information file, such as system.mdw, becomes corrupted or destroyed. In order to re-create users and groups and restore permissions to your databases and their objects, you must know the PID values. Microsoft Jet combines the user or group name and the PID value to create the identifier, called the SID, that it uses to control the permissions of databases and objects. By re-creating a user or group with its original name and PID value, Microsoft Jet will create a SID value identical to the original value, so all permissions set originally using that SID will work again. Because you cannot set the PID value using ADOX, the Microsoft Jet Provider effectively creates a new SID that does not have any database or object permissions set for it.

There are two methods for working around this problem if you choose not to explicitly set the PID values. One is to make frequent backups of the workgroup information file, and restore the file from the backup when necessary. This is usually the most efficient way to avoid the problem. The second method is to re-create all of the permissions in the database to work with the new users and groups that you will create. This is usually difficult to accomplish efficiently. Both methods do not require you to keep track of the PID values for users and groups.

Another way to avoid this problem is to use the new SQL Data Definition Language (DDL) security commands available for Microsoft Jet 4.0. The CREATE USER command, as implemented by Microsoft Jet 4.0, accepts a PID parameter for a new user. Likewise, the CREATE GROUP command accepts a PID parameter for a new group. The following code example demonstrates how to create a user and specify a PID value using ADO and DDL.

ADO

Sub ADOCreateUser2()

 

   Dim cmd As New ADODB.Command

 

   ' Create the Command

   cmd.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;Jet OLEDB:System database=" & _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW;" & _

      "User Id=Admin;Password=password;"

 

   ' Execute the DDL security command

   cmd.CommandText = "CREATE USER MyUser MyPW MyPID"

   cmd.Execute

 

End Sub

Adding a User to a Group

Adding users to a group makes maintaining permissions easier. Because users within a group inherit the permissions of the group, you can set permissions once and have it apply to an entire group of users. For example, you can assign update permissions for the Salary table to all managers by simply granting the Managers group update permission.

The following code example demonstrates how to create a new group and add an existing user to that group.

DAO

Sub DAOAddUserToNewGroup()

 

   Dim wks As DAO.Workspace

 

   ' Open the workspace

   DBEngine.SystemDB = _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   Set wks = DBEngine.CreateWorkspace("", "Admin", "password")

 

   ' Create a new group

   wks.Groups.Append wks.CreateGroup("MyGroup", "xMyGroup")

 

   ' Add the user to the new group

   wks.Users("MyUser").Groups.Append _

      wks.Users("MyUser").CreateGroup("MyGroup")

 

End Sub

ADOX

Sub ADOAddUserToNewGroup()

 

   Dim cat As New ADOX.Catalog

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;User Id=Admin;" & _

      "Password=password;Jet OLEDB:System database=" & _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

 

   ' Create a new group

   cat.Groups.Append "MyGroup"

 

   ' Add the user to the new group

   cat.Users("MyUser").Groups.Append "MyGroup"

 

End Sub

Both DAO and ADOX have a Groups collection on the Users object that can be used to add the user to a group as well as to determine what groups the user belongs to. However, note that with DAO you must create a new Group object using the User object's CreateGroup method before appending the Group object to the User object's Groups collection. With ADOX it is neither necessary nor valid to create a new Group object; just append the name of the group to the User object's Groups collection.

Note Before using this technique to create a Group, see the comments about setting PID values in the section "Creating Users and Groups."

Setting Permissions

By setting permissions you can control a user's access to an object. For example, you can allow one user to read an object's contents, but not change them. Permissions can be set for a specific user or an entire group of users. When permissions are set for a group, every user in that group inherits those permissions.

In the following example the user created in the section "Creating Users and Groups" is granted permissions to read, insert, update, and delete data.

DAO

Sub DAOSetUserObjectPermissions()

 

   Dim db As DAO.Database

   Dim wks As DAO.Workspace

   Dim doc As DAO.Document

 

   ' Open the database

   DBEngine.SystemDB = _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   Set wks = DBEngine.CreateWorkspace("", "Admin", "password")

   Set db = wks.OpenDatabase(".\NorthWind.mdb")

 

   ' Set permissions for MyUser on the Customers table

   Set doc = db.Containers("Tables").Documents("Customers")

   doc.UserName = "MyUser"

   doc.Permissions = dbSecRetrieveData Or dbSecInsertData _

      Or dbSecReplaceData Or dbSecDeleteData

 

End Sub

ADOX

Sub ADOSetUserObjectPermissions()

 

   Dim cat As New ADOX.Catalog

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;User Id=Admin;" & _

      "Password=password;Jet OLEDB:System database=" & _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

 

   ' Set permissions for MyUser on the Customers table

   cat.Users("MyUser").SetPermissions "Customers", adPermObjTable, _

      adAccessSet, adRightRead Or adRightInsert Or adRightUpdate _

      Or adRightDelete

 

End Sub

The process for setting permissions with ADOX is essentially the inverse of the DAO process. With DAO you first select the object and then indicate the user for whom to set permissions. With ADOX, you first select the user and then specify the object on which to set permissions.

In addition, with DAO you set a series of properties in order to set permissions on an object. In the previous example you set the UserName property followed by the Permissions property. With ADOX, a single method, SetPermissions, is used to set permissions on an object. The SetPermissions method has parameters that map to the properties used in DAO.

With the DAO Permissions property, which maps to the Rights parameter of the ADOX SetPermissions method, you supply a constant or combination of constants that represent the permissions to set. The following table shows how the DAO Security constants map to the ADOX Rights constants.

DAO

ADOX

dbSecNoAccess

adRightNone

dbSecFullAccess

adRightFull

dbSecDelete

adRightDrop

dbSecReadSec

adRightReadPermissions

dbSecWriteSec

adRightWritePermissions

dbSecWriteOwner

adRightWriteOwner

dbSecCreate

adRightCreate

dbSecReadDef

adRightReadDesign

dbSecWriteDef

adRightWriteDesign

dbSecRetrieveData

adRightRead

dbSecInsertData

adRightInsert

dbSecReplaceData

adRightUpdate

dbSecDeleteData

adRightDelete

dbSecDBAdmin

adRightFull

dbSecDBCreate

adRightCreate

dbSecDBExclusive

adRightExclusive

dbSecDBOpen

adRightRead

As shown in the preceding table, DAO has specific security constants for setting permissions on a database. These constants are used with the Databases container or a database object. In the following listings, you can see how to use both DAO and ADOX to set permissions for a user on a database object.

DAO

Sub DAOSetDatabasePermissions()

 

   Dim db As DAO.Database

   Dim wks As DAO.Workspace

   Dim doc As DAO.Document

 

   ' Open the database

   DBEngine.SystemDB = _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   Set wks = DBEngine.CreateWorkspace("", "Admin", "password")

   Set db = wks.OpenDatabase(".\NorthWind.mdb")

 

   ' Set permissions for MyUser on the current database

   Set doc = db.Containers("Databases").Documents("MSysDB")

   doc.UserName = "MyUser"

   doc.Permissions = dbSecDBExclusive

 

End Sub

ADOX

Sub ADOSetDatabasePermissions()

 

   Dim cat As New ADOX.Catalog

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;User Id=Admin;" & _

      "Password=password;Jet OLEDB:System database=" & _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

 

   ' Set permissions for MyUser on the current database

   cat.Users("MyUser").SetPermissions "", adPermObjDatabase, _

      adAccessSet, adRightExclusive

 

End Sub

Setting permissions for a database differs slightly from other objects. When using DAO, you must specify "MSysDb" as the article name when you want to specify permissions for the current database. To do the equivalent in ADOX, specify an empty string ("") as the name of the database.

In addition to granting permissions to a user on specific objects you may also want to specify permissions for a class/container of objects such as Tables. When specifying permissions on a container, you can indicate whether new objects of that class created by the user should inherit those permissions by default.

DAO

Sub DAOSetUserContainerPermissions()

 

   Dim db As DAO.Database

   Dim wks As DAO.Workspace

   Dim ctr As DAO.Container

 

   ' Open the database

   DBEngine.SystemDB = _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   Set wks = DBEngine.CreateWorkspace("", "Admin", "password")

   Set db = wks.OpenDatabase(".\NorthWind.mdb")

 

   ' Set permissions for MyUser on the Tables Container

   Set ctr = db.Containers("Tables")

   ctr.UserName = "MyUser"

   ctr.Inherit = True

   ctr.Permissions = dbSecRetrieveData Or dbSecInsertData _

      Or dbSecReplaceData Or dbSecDeleteData

 

End Sub

ADOX

Sub ADOSetUserContainerPermissions()

 

   Dim cat As New ADOX.Catalog

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;User Id=Admin;" & _

      "Password=password;Jet OLEDB:System database=" & _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

 

   ' Set permissions for MyUser on the Tables Container

   cat.Users("MyUser").SetPermissions Null, adPermObjTable, _

      adAccessSet, adRightRead Or adRightInsert Or adRightUpdate _

      Or adRightDelete, adInheritNone

 

End Sub

With DAO the Container object was used to specify permissions for a class of objects. With ADOX setting the Name parameter of the SetPermissions object to Null sets permissions for the class of objects specified by the ObjectType parameter. The InheritType parameter of the ADOX SetPermissions method indicates whether new objects should inherit the permissions. This is equivalent to setting the DAO Inherit property. In the ADOX example the InheritType parameter must be set to adInheritNone because it is setting the permissions for an existing object instead of a new object.

Use the Containers and Documents collections in DAO to set permissions on Access-specific objects, such as Forms, Reports, and Macros. Although ADOX supports setting permissions for Access-specific objects, the Microsoft Jet 4.0 Provider doesn't properly map the ADOX permissions flags to Microsoft Jet permissions flags. If you need to set permissions for Access-specific objects, you must use DAO until this problem is corrected.

Determining an Object's Owner

The database, and every object in the database, has an owner. By default, the owner is the user that created that object. The object owner has special privileges for that object in that he or she can always assign or revoke permissions for that object.

The following listings demonstrate how to get the user name of the object owner.

DAO

Sub DAOGetObjectOwner()

 

   Dim db As DAO.Database

   Dim wks As DAO.Workspace

 

   ' Open the database

   DBEngine.SystemDB = _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

   Set wks = DBEngine.CreateWorkspace("", "Admin", "password")

   Set db = wks.OpenDatabase(".\NorthWind.mdb")

 

   ' Print the owner of the Customers table

   Debug.Print db.Containers("Tables").Documents("Customers").Owner

 

End Sub

ADOX

Sub ADOGetObjectOwner()

 

   Dim cat As New ADOX.Catalog

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;User Id=Admin;" & _

      "Password=password;Jet OLEDB:System database=" & _

      "C:\Program Files\Microsoft Office\Office\SYSTEM.MDW"

 

   ' Print the owner of the Customers table

   Debug.Print cat.GetObjectOwner("Customers", adPermObjTable)

 

End Sub

With DAO you use the Owner property of an Document or Container object to retrieve the user name of the object owner. With ADOX you use the GetObjectOwner method of a Catalog object. This method takes the object's name and type as parameters.


Replication

Replication enables users at different locations to easily share the changes they are making to a database. Copies of a database, called replicas, can be made and distributed to users at different locations. Users at each location can work on their local copy and then share, or synchronize, their changes with users at other locations.

Note Use JRO, not ADO, to implement replication in your application.

Making a Database Replicable

The first step in enabling replication is to create a design master. A design master is the only replica in the replica set that can make both schema and data changes—all other replicas can only make data changes to replicated objects. Making a database replicable makes the database a design master.

The following listings demonstrate how to make an existing database replicable.

DAO

Sub DAOMakeDesignMaster()

 

   Dim dbsNorthwind As DAO.Database

   Dim prpNew As DAO.Property

 

   ' Open database for exclusive access.

   Set dbsNorthwind = DBEngine.OpenDatabase(".\NorthWind.mdb", True)

 

   With dbsNorthwind

      ' If Replicable property doesn't exist, create it.

 

      ' Turn on error handling in case property exists.

      On Error Resume Next

 

      ' Make the Northwind database replicable

      Set prpNew = .CreateProperty("Replicable", dbText, "T")

      .Properties.Append prpNew

 

      .Close

   End With

 

End Sub

JRO

Sub JROMakeDesignMaster()

 

   Dim repMaster As New JRO.Replica

 

   ' Make the Northwind database replicable.

   ' If successful, this will create a connection to the

   ' database.

   repMaster.MakeReplicable ".\NorthWind.mdb", False

 

   Set repMaster = Nothing

 

End Sub

The JRO model simplifies the code for making a database replicable. To make a database replicable using DAO, the database must be opened, the Replicable property must be created with the initial value set to "T," and finally the property must be appended to the Properties collection of the database. With JRO a database can be made replicable with a single method, MakeReplicable.

The MakeReplicable method in JRO has an optional second parameter named ColumnTracking set to False in the previous example. It indicates whether changes are tracked at the column level or row level. DAO did not expose the ability to track changes at the column level. Therefore, this parameter must be set to False if you want the same behavior as DAO. See the section "New Features in ADO, ADOX, and JRO" for more information on column-level tracking.

As with DAO, the process of making a database replicable using JRO cannot be reversed. It is recommended that you make a backup of your database before performing this operation.

Making Objects Local or Replicable

By default, when a database is made replicable all objects in that database will be replicated. If you do not want an object replicated you must indicate that the object should not be replicated (that is, it should remain local) before you make the database replicable.

In contrast, when you create a new table, query, form, report, macro, or module at a replica, the object is considered local and is stored only at that replica. If you want users at other replicas to be able to use the object, you must make it replicable.

This following listings demonstrate how to indicate that an object should be kept local when the database is made replicable.

DAO

Sub DAOKeepObjectLocal()

 

   Dim dbsNorthwind As DAO.Database

   Dim docTemp As DAO.Document

   Dim prpTemp As DAO.Property

 

   Set dbsNorthwind = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   Set docTemp = _

      dbsNorthwind.Containers("Tables").Documents("Contacts")

   Set prpTemp = docTemp.CreateProperty("KeepLocal", dbText, "T")

 

   docTemp.Properties.Append prpTemp

 

   dbsNorthwind.Close

 

End Sub

JRO

Sub JROKeepObjectLocal()

 

   Dim repMaster As New JRO.Replica

 

   repMaster.ActiveConnection = ".\NorthWind.mdb"

 

   repMaster.SetObjectReplicability "Contacts", "Tables", False

 

   Set repMaster = Nothing

 

End Sub

This next example shows how to make a new object in a replica replicable.

DAO

Sub DAOMakeObjectReplicable(strTable As String)

 

   Dim dbsNorthwind As DAO.Database

   Dim tdfTemp As DAO.TableDef

 

   Set dbsNorthwind = DBEngine.OpenDatabase(".\NorthWind.mdb")

   Set tdfTemp = dbsNorthwind.TableDefs(strTable)

 

   On Error GoTo ErrHandler

 

   tdfTemp.Properties("Replicable") = "T"

 

   On Error GoTo 0

 

   dbsNorthwind.Close

 

   Exit Sub

 

ErrHandler:

 

   Dim prpNew As DAO.Property

 

   If Err.Number = 3270 Then

      Set prpNew = tdfTemp.CreateProperty("Replicable", dbText, "T")

      tdfTemp.Properties.Append prpNew

   Else

      MsgBox "Error " & Err & ": " & Error

   End If

 

End Sub

JRO

Sub JROMakeObjectReplicable(strTable As String)

 

   Dim repMaster As New JRO.Replica

 

   repMaster.ActiveConnection = ".\NorthWind.mdb"

 

   repMaster.SetObjectReplicability strTable, "Tables", True

 

   Set repMaster = Nothing

 

End Sub

With DAO two properties, Replicable and KeepLocal, determine whether an object is or will be replicated. Use the KeepLocal property prior to making the database replicable to indicate that the object should not be made replicable when the database is made replicable. Use the Replicable property after the database is made replicable to indicate whether the object should be replicated. DAO requires you to create the properties using the CreateProperty method of the object's Properties collection before you can set them.

With JRO the GetObjectReplicability and SetObjectReplicability methods are used, both before and after the database is made replicable, to determine or set whether the object is or will be replicated. The method takes the name of the object you wish to get or set replicability for, the type of the object, and a Boolean value that indicates whether it should be kept local or made replicable.

The following pseudocode is the algorithm for mapping the DAO KeepLocal and Replicable properties to the ADO ObjectReplicability:

If DAO.Database.Replicable = 'T'

   If DAO.Object.Replicable = 'T'

      JRO.ObjectReplicability = True

   Else

      JRO.ObjectReplicability = False

Else

   If DAO.Object.KeepLocal = 'T'

      JRO.ObjectReplicability = False

   Else

      JRO.ObjectReplicability = True

Creating a Replica

The following listings demonstrate how to create a full, read/write replica of an existing replica using DAO and then using JRO.

DAO

Function DAOMakeAdditionalReplica(strReplicableDB As String, _

   strNewReplica As String) As Integer

 

   Dim dbsTemp As DAO.Database

 

   Set dbsTemp = DBEngine.OpenDatabase(strReplicableDB)

 

   dbsTemp.MakeReplica strNewReplica, "Replica of " & strReplicableDB

 

   dbsTemp.Close

 

End Function

JRO

Function JROMakeAdditionalReplica(strReplicableDB As String, _

   strNewReplica As String) As Integer

 

   Dim repMaster As New JRO.Replica

 

   repMaster.ActiveConnection = strReplicableDB

 

   repMaster.CreateReplica strNewReplica, "Replica of " & _

      strReplicableDB

 

   Set repMaster = Nothing

 

End Function

The code for creating a replica with JRO is similar to the DAO code. Both examples begin with opening or connecting to the design master. In DAO the design master is opened with the DBEngine object's OpenDatabase method. In ADO setting the Replica object's ActiveConnection property opens the design master. Once it is open, the new replica is created by calling a method to create the replica. The JRO equivalent to the DAO MakeReplica method is CreateReplica.

The DAO MakeReplica method has an optional parameter named Options. This parameter allows you to indicate the type of replica to create: full or partial, read-only or read/write.

In JRO there are two optional parameters named Type and Updatability. The Type parameter allows the user to indicate whether the replica should be full or partial. The Updatability parameter allows the user to indicate whether the replica is read-only or fully updatable.

The following table shows how the optional parameters and constants for the DAO MakeReplica method map to those for the JRO CreateReplica method.

DAO parameter

DAO constant

JRO parameter

JRO constant

Options

dbRepMakePartial

Type

jrRepTypePartial

Options

dbRepMakeReadOnly

Updatability

jrRepUpdReadOnly

The JRO CreateReplica method has two additional, optional parameters named Visibility and Priority. These parameters are omitted in the preceding JRO code example, indicating that the default value should be used. Visibility and Priority are new in JRO and provide additional control over how synchronizations with the replica will be performed. The default value for each of these parameters maps to the DAO behavior. See the section "New Features in ADO, ADOX, and JRO" for more information about replica visibility and priority.

Creating a Partial Replica

Sometimes, it is necessary to create replicas that contain a subset of the data contained in another replica. For example, a business might store its entire sales database at the headquarters office but replicate only regional data to its regional offices across the country. You can create a separate replica for each regional office that contains only the data relating to that region. The database at the headquarters office would be a full replica, with which each partial replica would be synchronized.

There are two ways to filter the data in a partial replica. The first method is by an expression, similar to an SQL WHERE clause (without the word WHERE). With an expression-based filter, the records in the table are limited to those that satisfy the expression. The second method to filter data is with a relationship filter. Relationship filters allow you to enforce the relationship when replicating data. It is generally used in conjunction with an expression-based filter.

The following listings demonstrate how to create a new partial replica and then populate the data in the partial replica limited by both an expression based filter and a relationship based filter.

DAO

Sub DAOCreatePartial()

 

   Dim dbsFull As DAO.Database

   Dim dbsPartial As DAO.Database

   Dim tdfCustomers As DAO.TableDef

   Dim relCustOrders As DAO.Relation

 

   ' Create partial replica.

   Set dbsFull = DBEngine.OpenDatabase(".\NorthWind.mdb")

   dbsFull.MakeReplica ".\FY96.mdb", "Partial Sales Replica", _

      dbRepMakePartial

   dbsFull.Close

 

   ' Create an expression based filter in the partial replica.

   Set dbsPartial = DBEngine.OpenDatabase(".\FY96.mdb", True)

   Set tdfCustomers = dbsPartial.TableDefs("Customers")

   tdfCustomers.ReplicaFilter = "Region = 'CA'"

 

   ' Create a filter based on a relationship in the partial replica.

   Set relCustOrders = dbsPartial.Relations("CustomersOrders")

   relCustOrders.PartialReplica = True

 

   ' Repopulate the partial replica based on the filters.

   dbsPartial.PopulatePartial ".\NorthWind.mdb"

 

   dbsPartial.Close

 

End Sub

JRO

Sub JROCreatePartial()

 

   Dim repFull As New JRO.Replica

   Dim repPartial As New JRO.Replica

 

   ' Create partial replica.

   repFull.ActiveConnection = ".\NorthWind.mdb"

   repFull.CreateReplica ".\FY96.mdb", "Partial Sales Replica", _

      jrRepTypePartial

   Set repFull = Nothing

 

   ' Create an expression based filter in the partial replica.

   ' The PopulatePartial method requires an exclusive connection

   repPartial.ActiveConnection = _

      "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\FY96.mdb;Mode=Share Exclusive"

   repPartial.Filters.Append "Customers", jrFilterTypeTable, _

      "Region = 'CA'"

 

   ' Create a filter based on a relationship in the partial replica.

   repPartial.Filters.Append "Orders", jrFilterTypeRelationship, _

      "CustomersOrders"

 

   ' Repopulate the partial replica based on the filters.

   repPartial.PopulatePartial ".\NorthWind.mdb"

 

   Set repPartial = Nothing

 

End Sub

The process for creating a partial replica is the same in JRO as it is in DAO. With both models the process is as follows: Create the partial replica, create the filter(s), and populate the partial replica using the filters. The primary difference between the two models is in creating the filters. DAO exposes properties of the Table and Relation objects for creating filters. JRO has a Filters collection. Use the Filters collection Append method to create new filters.

Listing Filters

The following listings demonstrate how to list all of the filters for a partial replica.

DAO

Sub DAOListFilters()

 

   Dim dbPartial As DAO.Database

   Dim tbl As DAO.TableDef

   Dim rel As DAO.Relation

 

   Set dbPartial = DBEngine.OpenDatabase(".\FY96.mdb")

 

   For Each tbl In dbPartial.TableDefs

      If tbl.ReplicaFilter <> "" Then

         Debug.Print tbl.Name & " : Table Filter : " & _

            tbl.ReplicaFilter

      End If

   Next

 

   For Each rel In dbPartial.Relations

      Debug.Print rel.Name & " : Relationship Filter";

      If rel.PartialReplica Then

         Debug.Print " : Partial";

      End If

      Debug.Print

   Next

 

   dbPartial.Close

 

End Sub

JRO

Sub JROListFilters()

 

   Dim repPartial As New JRO.Replica

   Dim flt As JRO.Filter

   Dim strFilterType As String

 

   repPartial.ActiveConnection = _

      "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=.\FY96.mdb"

 

   For Each flt In repPartial.Filters

      If flt.FilterType = jrFilterTypeTable Then

         strFilterType = "Table Filter"

      Else

         strFilterType = "Relationship Filter"

      End If

      Debug.Print flt.TableName & " : " & strFilterType & " : " & _

         flt.FilterCriteria

   Next

 

   Set repPartial = Nothing

 

End Sub

Synchronizing Data

Synchronizing two replicas involves exchanging data and design changes. Synchronization can be bi-directional (that is, changes in each replica are propagated to the other), or can occur in a single direction.

The following listing demonstrates how to synchronize changes between two replicas. The first example shows how to do a direct, two-way synchronization.

DAO

Sub DAOTwoWayDirectSync()

 

   Dim dbsNorthwind As DAO.Database

 

   Set dbsNorthwind = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Sends changes made in each replica to the other.

   dbsNorthwind.Synchronize ".\FY96.mdb", dbRepImpExpChanges

 

   dbsNorthwind.Close

 

End Sub

JRO

Sub JROTwoWayDirectSync()

 

   Dim repMaster As New JRO.Replica

 

   repMaster.ActiveConnection = ".\NorthWind.mdb"

 

   ' Sends changes made in each replica to the other.

   repMaster.Synchronize ".\FY96.mdb", jrSyncTypeImpExp, _

      jrSyncModeDirect

 

   Set repMaster = Nothing

 

End Sub

The following example shows how to do a two-way synchronization over the Internet.

DAO

Sub DAOInternetSync()

 

   Dim dbsTemp As DAO.Database

 

   Set dbsTemp = DBEngine.OpenDatabase(".\NewNorthWind.mdb")

 

   ' Synchronize the local database with the replica on

   ' the Internet server.

   dbsTemp.Synchronize "sampleserver/files/Northwind.mdb", _

      dbRepImpExpChanges Or dbRepSyncInternet

 

   dbsTemp.Close

 

End Sub

JRO

Sub JROInternetSync()

 

   Dim repMaster As New JRO.Replica

 

   repMaster.ActiveConnection = ".\NorthWind.mdb"

 

   ' Synchronize the local database with the replica on

   ' the Internet server.

   repMaster.Synchronize "sampleserver/files/Northwind.mdb", _

      jrSyncTypeImpExp, jrSyncModeInternet

 

   Set repMaster = Nothing

 

End Sub

For more information on Internet synchronization, read the article "Internet Synchronization with the Microsoft Jet Database Engine: A Technical Overview" available in the MSDN Library.

The JRO and DAO code for performing a two-way, direct synchronization between two replicas is similar. However, note that the JRO Synchronize method has an additional parameter that specifies jrSyncModeDirect. For functionality equivalent to DAO you must specify jrSyncModeDirect when calling the Synchronize method. In JRO if the SyncMode parameter is omitted, the synchronization will be performed indirectly. The ability to perform indirect synchronizations is a new feature in JRO designed to increase performance when synchronizing over a wide area network (WAN). See the section "New Features in ADO, ADOX, and JRO" for more information about performing indirect synchronizations.

The following table shows the mapping between the DAO Exchange parameter of the Synchronize method and the JRO SyncType and SyncMode parameters.

DAO parameter

DAO constant

JRO parameter

JRO constant

Exchange

dbRepExportChanges

SyncType

jrSyncTypeExport

Exchange

dbRepImportChanges

SyncType

jrSyncTypeImport

Exchange

dbRepImExpChanges

SyncType

jrSyncTypeImpExp

Exchange

dbRepSyncInternet

SyncMode

jrSyncModeInternet

Listing Synchronization Conflict Tables

If two users at two separate replicas each make a change to the same record in the database, a conflict may occur. If changes are being tracked at the row level, a conflict will occur if two users make a change to the same record. If changes are being tracked at the column level, a conflict will occur if two users make a change to the same column with a record. When a conflict occurs, the changes made by one user will fail to be applied to the other replica. Information regarding the conflict will be replicated to both replicas.

Information about the conflict is contained in a conflict table in each replica. Conflict tables contain the information that would have been placed in the table if the change had been successful. You can examine these conflict tables and work through them row by row, resolving the conflicts as appropriate.

The following listings demonstrate how to determine whether conflicts occurred during synchronization and, if conflicts did occur, how to retrieve the names of the conflict tables that were created.

DAO

Sub DAOConflictTables()

 

   Dim dbsNorthwind As DAO.Database

   Dim tdfTest As DAO.TableDef

   Dim bConflict As Boolean

 

   Set dbsNorthwind = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   bConflict = False

 

   ' Enumerate TableDefs collection and check ConflictTable

   ' property of each.

   For Each tdfTest In dbsNorthwind.TableDefs

      If tdfTest.ConflictTable <> "" Then

         ' There was a conflict with this table

         Debug.Print tdfTest.Name & " had a conflict."

         bConflict = True

      End If

   Next tdfTest

 

   ' If bConflict is still false then we didn't find any

   ' tables that had conflicts.

   If Not bConflict Then Debug.Print "No conflicts."

 

   dbsNorthwind.Close

 

End Sub

JRO

Sub JROConflictTables()

 

   Dim repMaster As New JRO.Replica

   Dim rstConflicts As ADODB.Recordset

 

   repMaster.ActiveConnection = ".\NorthWind.mdb"

 

   Set rstConflicts = repMaster.ConflictTables

 

   If rstConflicts.BOF And rstConflicts.EOF Then

      ' There are no conflict tables so no conflicts occurred.

      Debug.Print "No conflicts."

   Else

      Do Until rstConflicts.EOF

         Debug.Print rstConflicts.Fields(0) & " had a conflict."

         rstConflicts.MoveNext

      Loop

   End If

 

End Sub

With JRO the ConflictTables property of the Replica object is used to determine which tables had conflicts. This property returns an ADO Recordset object that contains one row for each table containing conflicts. With the ConflictTables property it is easy to determine whether conflicts occurred. If the Recordset is empty (the BOF and EOF properties of the Recordset are both true), no errors occurred. This differs from DAO in that with DAO you must check the ConflictTable property for each table in the TableDefs collection to determine whether conflicts occurred and what the name of the related conflict table is.

 


Miscellaneous

The next few sections show the differences between DAO and ADO for these tasks:

·         Handling Errors

·         Using Transactions

·         Compacting a Database

·         Refreshing the Cache

Handling Errors

There are two types of errors that can occur when executing ADO, ADOX, or JRO code: ADO errors and provider errors. ADO errors occur when you attempt to perform an invalid operation, such as trying to retrieve the tenth Field from the Recordset object's Field collection when the Fields collection only contains five fields.

Provider errors are errors generated by the OLE DB Provider or underlying data source. For example, specifying an invalid file name as the data source when trying to open a Microsoft Jet database will result in a provider error.

ADO errors are exposed by the run-time exception handling mechanism. In Visual Basic for Applications, an ADO error will trigger the On Error event and the Err object will contain information about the error. The ADO error will not create a new Error object in the Errors collection of the ADO Connection. OLE DB Provider errors will create new Error objects in the Errors Collection of the ADO Connection.

The Error object in both DAO and ADO is unlike the error variables and functions in Visual Basic in that more than one error can be generated by a single operation. The set of Error objects in the Errors collection describes one error.

The following code attempts to open a database that doesn't exist and then displays the error(s) that result.

DAO

Sub DAODatabaseError()

 

   On Error GoTo DAODatabaseError_Err

 

   Dim db As DAO.Database

   Dim errDB As DAO.Error

 

   Set db = DBEngine.OpenDatabase(".\NonExistent.mdb")

 

   Exit Sub

 

DAODatabaseError_Err:

   For Each errDB In DBEngine.Errors

      Debug.Print "Description: " & errDB.Description

      Debug.Print "Number: " & errDB.Number

      Debug.Print "JetErr: " & errDB.Number

   Next

 

End Sub

ADO

Sub ADODatabaseError()

 

   On Error GoTo ADODatabaseError_Err

 

   Dim cnn As New ADODB.Connection

   Dim errDB As ADODB.Error

 

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NonExistent.mdb"

 

   Exit Sub

 

ADODatabaseError_Err:

   For Each errDB In cnn.Errors

      Debug.Print "Description: " & errDB.Description

      Debug.Print "Number: " & errDB.Number & " (" & _

         Hex$(errDB.Number) & ")"

      Debug.Print "JetErr: " & errDB.SQLState

   Next

 

End Sub

The code is very similar. Note, however, that the ADO code will print two different error numbers. The first number is the ADO/OLE DB error code. This error code will be the same for similar errors regardless of the provider being used. This allows you to write ADO applications that can handle errors even when the provider is changed. The second number is a provider-specific error code. When using the Microsoft Jet Provider, this error number will be the same error number that DAO returns. However, other providers may return different numbers for this type of error.

Using Transactions

A transaction is defined as a "logical unit of work." Use transactions to enforce data integrity by making sure that multiple, related database operations are committed in an all-or-nothing manner. Microsoft Jet allows you to include both DML and DDL operations within a single transaction.

The following listing demonstrates how to use a transaction. It combines DML and DDL operations within a single transaction. If any part of the code fails, all changes will be rolled back. The code creates a new table named Contacts, populates it with data from the Customers table, adds a new column named ContactId to the Customers table, and then deletes the columns containing contact information from the Customers table.

DAO

Sub DAOTransactions()

 

   On Error GoTo DAOTransactions_Err

 

   Dim wks As DAO.Workspace

   Dim db As DAO.Database

   Dim tbl As DAO.TableDef

   Dim bTrans As Boolean

 

   ' Get the default workspace

   Set wks = DBEngine.Workspaces(0)

 

   ' Open the database

   Set db = wks.OpenDatabase(".\NorthWind.mdb")

 

   ' Begin the Transaction

   wks.BeginTrans

   bTrans = True

 

   ' Create the Contacts table.

   Set tbl = db.CreateTableDef("Contacts")

   With tbl

      ' Create fields and append them to the new TableDef object.

      ' This must be done before appending the TableDef object to

      ' the TableDefs collection of the Database.

      .Fields.Append .CreateField("ContactId", dbLong)

      .Fields("ContactId").Attributes = dbAutoIncrField

      .Fields.Append .CreateField("ContactName", dbText)

      .Fields.Append .CreateField("ContactTitle", dbText)

      .Fields.Append .CreateField("Phone", dbText)

      .Fields.Append .CreateField("Notes", dbMemo)

      .Fields("Notes").Required = False

   End With

   db.TableDefs.Append tbl

 

   ' Populate the Contacts table with information from the

   ' customers table

   db.Execute "INSERT INTO Contacts (ContactName, ContactTitle," & _

      "Phone) SELECT DISTINCTROW [Customers].[ContactName], " & _

      "[Customers].[ContactTitle], [Customers].[Phone] " & _

      "FROM Customers;"

 

   ' Add a ContactId field to the Customers Table

   Set tbl = db.TableDefs("Customers")

   tbl.Fields.Append tbl.CreateField("ContactId", dbLong)

 

   ' Populate the Customers table with the appropriate ContactId

   db.Execute "UPDATE DISTINCTROW Contacts INNER JOIN Customers " & _

      "ON Contacts.ContactName = Customers.ContactName SET " & _

      "Customers.ContactId = [Contacts].[ContactId];"

 

   ' Delete the ContactName, ContactTitle, and Phone columns from

   ' Customers

   tbl.Fields.Delete "ContactName"

   tbl.Fields.Delete "ContactTitle"

   tbl.Fields.Delete "Phone"

 

   ' Commit the transaction

   wks.CommitTrans

 

   Exit Sub

 

DAOTransactions_Err:

   If bTrans Then wks.Rollback

 

   Debug.Print DBEngine.Errors(0).Description

   Debug.Print DBEngine.Errors(0).Number

 

End Sub

ADO

Sub ADOTransactions()

 

   On Error GoTo ADOTransactions_Err

 

   Dim cnn As New ADODB.Connection

   Dim cat As New ADOX.Catalog

   Dim tbl As New ADOX.Table

   Dim bTrans As Boolean

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Begin the Transaction

   cnn.BeginTrans

   bTrans = True

 

   Set cat.ActiveConnection = cnn

 

   ' Create the Contacts table

   With tbl

      .Name = "Contacts"

      Set .ParentCatalog = cat

      .Columns.Append "ContactId", adInteger

      .Columns("ContactId").Properties("AutoIncrement") = True

      .Columns.Append "ContactName", adWChar

      .Columns.Append "ContactTitle", adWChar

      .Columns.Append "Phone", adWChar

      .Columns.Append "Notes", adLongVarWChar

      .Columns("Notes").Attributes = adColNullable

   End With

   cat.Tables.Append tbl

 

   ' Populate the Contacts table with information from the

   ' customers table

   cnn.Execute "INSERT INTO Contacts (ContactName, ContactTitle," & _

      "Phone) SELECT DISTINCTROW Customers.ContactName," & _

      "Customers.ContactTitle, Customers.Phone FROM Customers;"

 

   ' Add a ContactId field to the Customers Table

   Set tbl = cat.Tables("Customers")

   tbl.Columns.Append "ContactId", adInteger

 

   ' Populate the Customers table with the appropriate ContactId

   cnn.Execute "UPDATE DISTINCTROW Contacts INNER JOIN Customers " _

      & "ON Contacts.ContactName = Customers.ContactName SET " & _

      "Customers.ContactId = [Contacts].[ContactId];"

 

   ' Delete the ContactName, ContactTitle, and Phone columns

   ' from Customers

   tbl.Columns.Delete "ContactName"

   tbl.Columns.Delete "ContactTitle"

   tbl.Columns.Delete "Phone"

 

   ' Commit the transaction

   cnn.CommitTrans

 

   Exit Sub

 

ADOTransactions_Err:

   If bTrans Then cnn.RollbackTrans

 

   Debug.Print cnn.Errors(0).Description

   Debug.Print cnn.Errors(0).Number

   Debug.Print cnn.Errors(0).SQLState

 

End Sub

Both DAO and ADO have similar methods for beginning, committing, and rolling back a transaction. One difference to note, however, is that because DAO transactions are tied to the Workspace object, it is possible to use DAO to perform a transaction that spans multiple Microsoft Jet databases. ADO transactions are tied to the Connection object, which limits the transaction to a single data source.

DAO also supports an additional parameter to the CommitTrans method: dbForceOSFlush. This forces the database engine to immediately flush all updates to disk, instead of caching them temporarily. The Microsoft Jet Provider exposes a property, "Jet OLEDB:Transaction Commit Mode," in the Connection object's Properties collection, which allows you to specify that transactions within that connection should flush all updates to disk upon commit. Setting this property to 1 is equivalent to using the dbForceOSFlush parameter.

Compacting a Database

As a database file is used, it can become fragmented as objects and records are created and deleted. Periodic defragmentation reduces the amount of wasted space in the file and can enhance performance. Compacting can also repair a corrupted database.

The following listings demonstrate how to compact a database.

Note Use JRO, not ADO, to compact a database.

DAO

Sub DAOCompactDatabase()

 

   ' Make sure there isn't already a file with the

   ' name of the compacted database.

   If Dir(".\NewNorthWind.mdb") <> "" Then Kill ".\NewNorthWind.mdb"

 

   ' Basic compact - creating new database named newnwind

   DBEngine.CompactDatabase ".\NorthWind.mdb", ".\NewNorthWind.mdb"

 

   ' Delete the original database

   Kill ".\NorthWind.mdb"

 

   ' Rename the file back to the original name

   Name ".\NewNorthWind.mdb" As ".\NorthWind.mdb"

 

End Sub

JRO

Sub JROCompactDatabase()

 

   Dim je As New JRO.JetEngine

 

   ' Make sure there isn't already a file with the

   ' name of the compacted database.

   If Dir(".\NewNorthWind.mdb") <> "" Then Kill ".\NewNorthWind.mdb"

 

   ' Compact the database

   je.CompactDatabase "Data Source=.\NorthWind.mdb;", _

      "Data Source=.\NewNorthWind.mdb;"

 

   ' Delete the original database

   Kill ".\NorthWind.mdb"

 

   ' Rename the file back to the original name

   Name ".\NewNorthWind.mdb" As ".\NorthWind.mdb"

 

End Sub

The JRO CompactDatabase method takes two connection strings, which indicate the source database and destination database respectively. See the JRO online Help for more information on the JRO CompactDatabase method.

In addition to defragmenting or repairing your database, CompactDatabase can also be used to change the database password, convert the database from an older Microsoft Jet version to a new version, to encrypt or decrypt the database, or to change the locale of the database. Note that CompactDatabase will automatically convert older databases to the current version, unless you use the Jet OLEDB:Engine Type property in the destination database parameter.

The following code demonstrates how to encrypt a database.

DAO

Sub DAOEncryptDatabase()

 

   ' Use compact to create a new, encrypted version of the database

   DBEngine.CompactDatabase ".\NorthWind.mdb", _

      ".\NewNorthWind.mdb", , dbEncrypt

 

End Sub

JRO

Sub JROEncryptDatabase()

 

   Dim je As New JRO.JetEngine

 

   ' Use compact to create a new, encrypted version of the database

   je.CompactDatabase "Data Source=.\NorthWind.mdb;", _

      "Data Source=.\NewNorthWind.mdb;" & _

      "Jet OLEDB:Encrypt Database=True"

 

End Sub

Refreshing the Cache

Microsoft Jet maintains an internal cache of records for each Microsoft Jet session. Caching records provides a significant performance improvement, but it means that other sessions may not immediately see changes.

In DAO a session is associated with a DBEngine object. Because each application can only have one DBEngine object, this means that each application will have its own session. A given application using DAO will always see its own changes, but other applications may not see the changes immediately. In ADO a session is associated with a Connection object. A single application using ADO may have multiple Connection objects. So, within a single application, changes may not been seen immediately.

There may be instances where performance is less important than guaranteeing that a Recordset contains the latest data. In those instances, it makes sense to force a refresh of Microsoft Jet's internal cache. Both DAO and JRO provide a mechanism for this. In DAO use the DBEngine object's Idle method with dbRefreshCache to force Microsoft Jet to refresh its cache. With JRO use the JetEngine object's RefreshCache method, passing in the ADO connection as a parameter.

The following listings demonstrate how to refresh the cache using DAO and JRO.

DAO

Sub DAORefreshCache()

 

   Dim db As DAO.Database

   Dim rst As DAO.Recordset

   Dim fld As DAO.Field

 

   ' Open the database

   Set db = DBEngine.OpenDatabase(".\NorthWind.mdb")

 

   ' Refresh the cache to ensure that the latest data

   ' is available.

   DBEngine.Idle dbRefreshCache

 

   Set rst = db.OpenRecordset("SELECT * FROM Shippers")

   Do Until rst.EOF

      For Each fld In rst.Fields

         Debug.Print fld.Value;

      Next

      Debug.Print

      rst.MoveNext

   Loop

   rst.Close

 

End Sub

ADO

Sub JRORefreshCache()

 

   Dim cnn As New ADODB.Connection

   Dim rst As ADODB.Recordset

   Dim fld As ADODB.Field

   Dim je As New JRO.JetEngine

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Refresh the cache to ensure that the latest data

   ' is available.

   je.RefreshCache cnn

 

   ' Open a recordset and read the data

   Set rst = cnn.Execute("SELECT * FROM Shippers")

   Do Until rst.EOF

      For Each fld In rst.Fields

         Debug.Print fld.Value;

      Next

      Debug.Print

      rst.MoveNext

   Loop

   rst.Close

 

End Sub

This preceding example is somewhat contrived because the cache will most likely already contain the latest data as the Database and Connection are being opened for the first time immediately before attempting to open the Recordset. The ability to refresh the cache is generally more useful when a Database or Connection is opened at the time the application is first launched, and then at some later point a Recordset with the latest data needs to be opened.

 


New Features in ADO, ADOX, and JRO

The following sections describe new features in ADO, ADOX, JRO, and the Microsoft Jet Provider. The functionality exposed by these features is not available in DAO. This section is not intended as a complete list of additional features exposed in ADO, but rather serves to highlight some of the new functionality.

Creatable Recordset Objects

Often, a developer finds a need for a place to temporarily store some data, or wants some data to act like it came from a server so it can participate in data binding in a user interface.

ADO (in conjunction with the Cursor Service for OLE DB) enables the developer to build an empty Recordset object by specifying column information and calling Open. The following code demonstrates this:

Sub ADOCreateRecordset()

 

   Dim rst As New ADODB.Recordset

 

   rst.CursorLocation = adUseClient

 

   ' Add Some Fields

   rst.Fields.Append "dbkey", adInteger

   rst.Fields.Append "field1", adVarChar, 40, adFldIsNullable

   rst.Fields.Append "field2", adDate

 

   ' Create the Recordset

   rst.Open , , adOpenStatic, adLockBatchOptimistic

 

   ' Add Some Rows

   rst.AddNew Array("dbkey", "field1", "field2"), _

      Array(1, "string1", Date)

   rst.AddNew Array("dbkey", "field1", "field2"), _

      Array(2, "string2", #1/6/1992#)

 

   ' Look at the values -

   ' a value of 1 for status column = newly record

   rst.MoveFirst

   Debug.Print "Status", "dbkey", "field1", "field2"

   Do Until rst.EOF

      Debug.Print rst.Status, rst!dbkey, rst!field1, rst!field2

      rst.MoveNext

   Loop

 

   ' Commit the rows without ActiveConnection

   ' set resets the status bits

   rst.UpdateBatch adAffectAll

 

   ' Change the first of the two rows

   rst.MoveFirst

   rst!field1 = "changed"

 

   ' Now look at the status, first row shows 2 (modified row),

   ' second shows 8 (no modifications)

   ' Also note that the OriginalValue property shows the value

   ' before the modification

   rst.MoveFirst

   Do Until rst.EOF

      Debug.Print

      Debug.Print rst.Status, rst!dbkey, rst!field1, rst!field2

      Debug.Print , rst!dbkey.OriginalValue, _

         rst!field1.OriginalValue, rst!field2.OriginalValue

      rst.MoveNext

   Loop

 

End Sub

Another feature of a creatable recordset is that pending operations can be committed to the Recordset. Any time UpdateBatch is called on a client cursor that has no ActiveConnection set, the changes in the affected row (controlled by the AffectedRows parameter) will be committed to the buffer and the Status flags will be reset. The same applies to CancelBatch, except the changes in the buffer will be reverted and the flag will be reset.

Microsoft Data Links

Microsoft Data Links provides a graphical user interface that enables the user to create, edit, and organize connections to a data source. A Data Link file for c:\nwind.mdb can be created as follows:

7.        In Windows Explorer, select the folder in which you want to create the new data link. For example, select the C:\ folder to create the data link file in the root directory of the C drive.

8.        Choose New from the File menu of Windows Explorer.

9.        Choose Microsoft Data Link.

10.     Rename the file "nwind.udl."

11.     Double-click the new file to open the Data Link Properties window.

12.     Select the Provider tab.

13.     Select Microsoft Jet 4.0 OLE DB Provider from the list.

14.     Select the Connection tab.

15.     Enter the path to the Northwind database (for example, c:\nwind.mdb) in the first text box.

The following code shows how to use the data link to open the connection rather than providing the connection information directly:

Sub ADOUseExistingDataLink()

 

   ' Opens an ADO Connection using a Data Links file (UDL)

 

   Dim cnn As New ADODB.Connection

   Dim rs As New ADODB.Recordset

 

   cnn.Open "File Name=.\NorthWind.udl;"

 

   rs.Open "Customers", cnn, adOpenKeyset, adLockReadOnly

 

   rs.MoveLast

   Debug.Print rs.RecordCount

 

   rs.Close

   cnn.Close

 

End Sub

It is also possible to use Microsoft Data Links to prompt the user for connection information. The following code demonstrates how to launch the Microsoft Data Links UI from code. In order to run this code, you'll need to add a reference to Microsoft OLE DB Service Component 1.0 Type Library in your project.

Private Sub Command1_Click()

 

   Dim cnn As New ADODB.Connection

   Dim dl As New DataLinks

 

   On Error Resume Next

 

   dl.hWnd = Me.hWnd

   If dl.PromptEdit(cnn) Then

      cnn.Open

   End If

 

   cnn.Close

 

End Sub

The previous code example could be modified to first specify a default value for the provider and data source.

Private Sub Command2_Click()

 

   Dim cnn As New ADODB.Connection

   Dim dl As New DataLinks

 

   On Error Resume Next

 

   cnn.Provider = "Microsoft.Jet.OLEDB.4.0;"

   cnn.Properties("Data Source") = ".\NorthWind.mdb"

   dl.hWnd = Me.hWnd

   If dl.PromptEdit(cnn) Then

      cnn.Open

   End If

 

   cnn.Close

 

End Sub

User Roster

Many database maintenance activities require that the administrator have exclusive access to the database. The database cannot be opened exclusively if other people already have the database open. With DAO the administrator had no way of determining who was logged on to the database, making it difficult to determine who was blocking the administrator's attempt to open the database exclusively.

ADO and the Microsoft Jet Provider expose a schema rowset that contains information about who currently has the database open. This is a provider-specific schema rowset named DBSCHEMA_JETOLEDB_USERROSTER. The following code demonstrates how to open this schema rowset using ADO:

Sub ADOUserRoster()

 

   Dim cnn As New ADODB.Connection

   Dim rst As ADODB.Recordset

 

   ' Open the connection

   cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Open the user roster schema rowset

   Set rst = cnn.OpenSchema(adSchemaProviderSpecific, , _

      JET_SCHEMA_USERROSTER)

 

   ' Print the results to the debug window

   Debug.Print rst.GetString

 

   cnn.Close

 

End Sub

The first parameter, QueryType, to the ADO OpenSchema method takes an enumeration value. Values are defined for the schema rowsets defined in the OLE DB specification. To use a provider-specific schema rowset such as DBSCHEMA_JETOLEDB_USERROSTER, you must specify adProviderSpecific and then provide the GUID for the schema rowset as the last parameter. In this example the constant JET_SCHEMA_USERROSTER is used in place of the GUID. This constant is contained in the JetOLEDBConstants.txt file included in "Appendix C: Microsoft Jet 4.0 OLE DB Provider-Defined Property Values".

The following table describes the information contained in each column of the schema rowset.

Column

Description

COMPUTER_NAME

The name of the workstation as specified using the Network icon in Control Panel.

LOGIN_NAME

The name of the user used to log on to the database if the database has been secured; otherwise, the default value will be Admin.

CONNECTED

True, if there is a corresponding user lock in the .ldb file.

SUSPECTED_STATE

True, if the user has left the database in a suspect state; otherwise, Null.

Enhanced Auto-Increment (Counter) Columns

Microsoft Jet 4.0 includes enhanced support for auto-increment columns that allows you to specify an initial value for the column, also known as the seed value, as well as a value by which to increment the column.

The following code demonstrates how to create a new auto-increment column with an initial value of 10 and an increment value of 100. It assumes the Contacts table already exists. To create this table, run the ADOCreateTable example code in the section "Creating and Modifying Tables" earlier in this article.

Sub ADOCreateEnhancedAutoIncrColumn()

 

   Dim cat As New ADOX.Catalog

   Dim col As New ADOX.Column

 

   ' Open the catalog

   cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _

      "Data Source=.\NorthWind.mdb;"

 

   ' Create the new auto increment column

   With col

      .Name = "ContactId"

      .Type = adInteger

      Set .ParentCatalog = cat

      .Properties("AutoIncrement") = True

      .Properties("Seed") = CLng(10)

      .Properties("Increment") = CLng(100)

   End With

 

   ' Append the column to the table

   cat.Tables("Contacts").Columns.Append col

 

   Set cat = Nothing

 

End Sub

In addition to specifying seed and increment values when the column is created, they can be modified for existing auto-increment columns. Use caution when modifying these values for existing columns as it is possible to create conflicts with existing values. For example, if the table already contains values 1 through 10 in the column, it is possible to set the seed value to 5.

Replication

Replica Visibility

JRO introduces a new property of a replica that is used to indicate the visibility of a replica. The visibility determines which replicas that replica can synchronize with. A replica's visibility may be Global, Local, or Anonymous. The replica's visibility is set when the replica is first created. Once the replica is created the visibility cannot be changed.

A Global replica can synchronize with any other replica in the set. Changes at a Global replica are fully tracked. From a Global replica, you can create replicas that are Global, Local, or Anonymous. Replicas created from a Global replica are global by default.

A Local replica can synchronize only with its parent, a Global replica, and will not be permitted to synchronize with other replicas in the replica set. The parent will proxy any replication conflicts and errors for the Local replica. Other replicas will not be aware of the Local replica. The parent replica can schedule a synchronization with a Local replica. All replicas created from a Local replica will also be local and inherit the same parent replica.

An Anonymous replica can synchronize with its parent, a Global replica. These are replicas who, say, subscribe by way of the Internet, who do not have any particular identity, but instead proxy their identity for updates to the publishing replica. A Global replica will not be able to schedule synchronizations to an Anonymous replica. Anonymous replicas provide a way of getting around the "limit on number of replicas" problem. In addition, it helps to keep out unnecessary topology information about replicas that participate only occasionally. All replicas created from an Anonymous replica will also be anonymous and inherit the same parent replica.

The following code demonstrates how to create a new Anonymous replica:

Function JROMakeAnonReplica(strReplicableDB As String, _

   strNewReplica As String) As Integer

 

   Dim repMaster As New JRO.Replica

 

   repMaster.ActiveConnection = strReplicableDB

 

   repMaster.CreateReplica strNewReplica, "Replica of " & _

      strReplicableDB, , jrRepVisibilityAnon

 

   Set repMaster = Nothing

 

End Function

Replica Priority

JRO introduces a new property of a replica that is used to indicate the relative importance of a replica during synchronization. If conflicts are encountered during synchronization the replica with the highest priority wins.

The following code demonstrates how to set the priority when creating a new replica:

Function JROMakeAdditionalReplica2(strReplicableDB As String, _

   strNewReplica As String, intPriority As Integer) As Integer

 

   Dim repMaster As New JRO.Replica

 

   repMaster.ActiveConnection = strReplicableDB

 

   repMaster.CreateReplica strNewReplica, "Replica of " & _

      strReplicableDB, , , intPriority

 

   Set repMaster = Nothing

 

End Function

Indirect Synchronization

With a direct synchronization, your machine is tied up until the synchronization is complete. On fast local area networks (LANs) this may not be an issue. However, synchronization over a slow wide area network (WAN) may take many minutes or more. Indirect synchronization was designed for this scenario. For an indirect synchronization, the synchronizer leaves the changes in a drop box and control returns to the application. The synchronizer for the other replica will then pick up the changes and apply them.

The following code demonstrates how to perform an indirect synchronization:

Sub JROTwoWayIndirectSync()

 

   Dim repMaster As New JRO.Replica

 

   repMaster.ActiveConnection = ".\NorthWind.mdb"

 

   ' Sends changes made in each replica to the other.

   repMaster.Synchronize ".\NewNorthWind.mdb", jrSyncTypeImpExp, _

      jrSyncModeIndirect

 

   Set repMaster = Nothing

 

End Sub

Synchronizing Changes with Microsoft SQL Server

JRO supports synchronizing changes between a Microsoft SQL Server and a Microsoft Jet database. Note that the Microsoft Jet database and its synchronizer must already be configured to support the replication to SQL Server.

The following code demonstrates how to perform a Microsoft Jet to SQL synchronization:

Sub JROJetSQLSync()

 

   Dim repMaster As New JRO.Replica

 

   repMaster.ActiveConnection = ".\Pubs.mdb"

 

   ' Sends changes made in each replica to the other.

   repMaster.Synchronize "", jrSyncTypeImpExp, jrSyncModeDirect

 

   Set repMaster = Nothing

 

End Sub

Notice the TargetReplica parameter for the Synchronize method is an empty string ("") and the SyncMode is jrSyncModeDirect. Leaving the TargetReplica blank indicates that this is a Microsoft Jet-to-SQL Server synchronization. All Microsoft Jet-to-SQL Server synchronizations are direct.

Column-Level Conflict Resolution

Column-level conflict resolution lets you merge two records and only report a conflict if simultaneous changes have been to the same field. If you frequently have overlapping updates in the same row, setting this option could increase performance.

This option is set when a database is made replicable—it cannot be changed once the process of making the database replicable is complete. Column-level conflict resolution is turned on by default.

The following code demonstrates how to turn on column-level tracking when making a database replicable:

Sub JROMakeDesignMaster2()

 

   Dim repMaster As New JRO.Replica

 

   repMaster.MakeReplicable ".\NorthWind.mdb", True

 

   Set repMaster = Nothing

 

End Sub

For an example of how to turn off column-level tracking when making a database replicable, see the JRO code example in the section "Making a Database Replicable."

 


Obsolete Properties and Methods

This following section describes DAO properties and methods that don't map to properties or methods in ADO, ADOX, or JRO. However, that does not imply that the functionality provided by the DAO properties and methods is not available in ADO, ADOX, or JRO.

In the table to follow each property or method not exposed is listed and followed by a description of why it is not exposed and, if applicable, how to get the equivalent functionality using ADO, ADOX, or JRO.

Object

Property/
method

Explanation

DBEngine

DefaultType

DAO 3.5 introduced ODBCDirect as a means to work with ODBC data sources without loading the Microsoft Jet database engine. To use ODBCDirect, you set the DefaultType and/or Type properties to dbUseODBC.

As discussed in the "Introduction" section, ADO has a different approach to enabling access to ODBC data sources as well as enabling native access to various data sources such as Microsoft SQL Server. ADO allows users to choose which OLE DB Provider they want to use to access the data. So, to work with ODBC data sources without loading the Microsoft Jet database engine, specify MSDASQL rather than Microsoft.Jet.OLEDB.4.0 as the provider name.

DBEngine

DefaultPassword

In DAO the DefaultPassword property assigns the default password for a new workspace created on the DBEngine object. Such a property doesn't make sense in the context of the ADO connection model.

DBEngine

DefaultUser

In DAO the DefaultUser property assigns the default user ID for a new workspace created on the DBEngine object. Such a property doesn't make sense in the context of the ADO connection model.

DBEngine

RegisterDatabase

The ODBC API provides this functionality. Users should call the ODBC API directly.

DBEngine

RepairDatabase

The functionality found in RepairDatabase has been incorporated into CompactDatabase in Microsoft Jet 4.0. Compacting a database will also repair it.

Workspace

Name

The Name property identifies a workspace in the Workspaces collection. Because there is no concept of a collection of connections in ADO, this property doesn't apply.

Workspace

Type

See comments for DBEngine object DefaultType property.

Database

V1xNullBehavior

Support for version 1.0 Null behavior has been dropped. This functionality is obsolete.

Recordset

CacheStart

Using ADO and the Microsoft Jet Provider, it is not necessary to explicitly set the CacheStart property. The Microsoft Jet Provider will start caching from the current record.

Recordset

Edit

The process of updating records has been simplified with ADO such that Edit is not needed. With DAO you had to call the Edit method to put the Recordset into edit mode before modifying a value; otherwise, an error would occur. With ADO modifying a value automatically puts the Recordset in edit mode.

Recordset

FillCache

As with the CacheStart property, it is not necessary with ADO and the Microsoft Jet Provider to explicitly set the FillCache property. The Microsoft Jet Provider will start caching from the current record.

Recordset

LastModified

After modifying a record (or creating a new one) and calling the Update method to save the changes, DAO users had to set the Bookmark property to the LastModified property to ensure that the current record was the record they had just modified. With ADO this is not necessary because ADO automatically ensures that the current record stays the same after a call to Update.

Recordset

Name

ADO does not have a Recordset collection so there is no need for a name apart from the name the user has given the Recordset when it was declared.

Recordset

Restartable

All ADO Recordset objects are restartable, so a property indicating whether it can be resarted is not necessary.

QueryDef

ReturnsRecords

With DAO QueryDef objects it was necessary to know whether the query returned records in order to execute the query. If the query returned records, you had to use the OpenRecordset method to execute the query. If it did not return records, you had to use the Execute method.

With ADO you no longer need to know whether the query returns records in order to execute it because the Execute method is used in either case. If the query returns records, the Execute method returns a Recordset object; otherwise, it returns Nothing.

Container

Name

The Container object's functionality can be found as part of the User and Group objects.

Document

Name

The Document object's functionality can be found as properties on the objects themselves or as part of the User and Group objects.

User

Password

The user's Password is part of the connection string.

User

PID

PIDs are only required to rebuild a corrupted or destroyed workgroup information file. Keep a current backup of workgroup information files to avoid this, or use SQL DDL commands to specify the PID for a new user.

Group

PID

PIDs are only required to rebuild a corrupted or destroyed workgroup information file. Keep a current backup of workgroup information files to avoid this, or use SQL DDL commands to specify the PID for a new group.

 


Conclusion

In the past, DAO made working with tabular databases easier than calling dynamic-link library (DLL) functions by wrapping up those functions with objects. This made data access relatively simple for most developers. ADO uses this strength of DAO and expands on it. ADO uses OLE DB to work with data sources, so you will be able to reach many more data sources than with DAO. Many of these data sources won't be stored as traditional, tabular databases. In those cases, using DAO won't be possible. ADO is similar enough to DAO that you won't need to relearn an entire object model to handle these new data sources. Once you've learned ADO, you're ready for the future of data access.

 


Appendix A: DAO to ADO Quick Reference

The following table is intended to be a quick reference for determining how to map DAO properties and methods to ADO, ADOX, and JRO properties and methods. However, it is not intended to imply a direct, one-to-one mapping between the properties and methods listed. There may be subtle, or not so subtle, differences between the mapped properties and methods. For more detailed information on the ADO, ADOX, and JRO properties and methods, see the documentation for the object model. Use the information provided earlier in this article to map the code for common tasks that are performed using DAO to ADO, ADOX, and JRO code.

DAO object

Property/method

ADO/ADOX/
JROModel

Object

Property/method

DBEngine

DefaultType1

N/A

N/A

N/A

DBEngine

DefaultPassword1

N/A

N/A

N/A

DBEngine

DefaultUser1

N/A

N/A

N/A

DBEngine

IniPath

ADO

Connection

Jet OLEDB:Registry Path2

DBEngine

LoginTimeout

ADO

Connection

ConnectionTimeout

DBEngine

SystemDB

ADO

Connection

Jet OLEDB:System Database2

DBEngine

Version

ADO

Connection

Version

DBEngine

BeginTrans

ADO

Connection

BeginTrans

DBEngine

CommitTrans

ADO

Connection

CommitTrans

DBEngine

Rollback

ADO

Connection

RollbackTrans

DBEngine

CompactDatabase

JRO

JetEngine

CompactDatabase

DBEngine

CreateDatabase

ADOX

Catalog

Create

DBEngine

CreateWorkspace

ADO

Connection

Open

DBEngine

Idle

JRO

JetEngine

RefreshCache

DBEngine

OpenDatabase

ADO

Connection

Open

DBEngine

RegisterDatabase1

N/A

N/A

N/A

DBEngine

RepairDatabase1

N/A

N/A

N/A

DBEngine

SetOption

ADO

Connection

Properties3

Workspace

IsolateODBCTrans

ADO

Connection

Isolation Levels2

Workspace

LoginTimeout

ADO

Connection

ConnectionTimeout

Workspace

Name1

N/A

N/A

N/A

Workspace

Type1

N/A

N/A

N/A

Workspace

UserName

ADO

Connection

User Id2

Workspace

BeginTrans

ADO

Connection

BeginTrans

Workspace

CommitTrans

ADO

Connection

CommitTrans

Workspace

Rollback

ADO

Connection

RollbackTrans

Workspace

Close

ADO

Connection

Close

Workspace

CreateDatabase

ADOX

Catalog

Create

Workspace

CreateGroup

ADOX

Groups

Append

Workspace

CreateUser

ADOX

Users

Append

Workspace

OpenDatabase

ADO

Connection

Open

Database

CollatingOrder

ADO

Connection

Locale Identifier2

Database

Connect

ADO

Connection

ConnectionString

Database

Name

ADO

Connection

Data Source2

Database

QueryTimeout

ADO

Connection

CommandTimeout

Database

Replicable

JRO

Replica

MakeReplicable

Database

ReplicaId

JRO

Replica

ReplicaId

Database

ReplicationConflictFunction

JRO

Replica

ConflictFunction

Database

RecordsAffected

ADO

Connection

Execute(RecordsAffected)

Database

Transactions

ADO

Connection

Transaction DDL2

Database

Updatable

ADO

Connection

Mode

Database

V1xNullBehavior

N/A

N/A

N/A

Database

Version

ADO

Connection

DBMS Version2

Database

Close

ADO

Connection

Close

Database

CreateProperty

N/A

N/A

Not supported in this release

Database

CreateQueryDef

ADOX

Command

Dim New4

Database

CreateRelation

ADOX

Key

Dim New4

Database

CreateTableDef

ADOX

Table

Dim New4

Database

Execute

ADO

Connection

Execute

Database

MakeReplica

JRO

Replica

CreateReplica

Database

NewPassword

ADOX

Catalog

Modify

Database

OpenRecordset

ADO

Recordset

Open

Database

PopulatePartial

JRO

Replica

PopulatePartial

Database

Synchronize

JRO

Replica

Synchronize

Recordset

AbsolutePosition

ADO

Recordset

AbsolutePosition

Recordset

BOF

ADO

Recordset

BOF

Recordset

EOF

ADO

Recordset

EOF

Recordset

Bookmark

ADO

Recordset

Bookmark

Recordset

Bookmarkable

ADO

Recordset

Supports

Recordset

CacheSize

ADO

Recordset

Jet OLEDB:Fat Cursor Cache Size2

Recordset

CacheStart1

N/A

N/A

N/A

Recordset

DateCreated

ADOX

Table

DateCreated

Recordset

LastUpdated

ADOX

Table

DateModified

Recordset

EditMode

ADO

Recordset

EditMode

Recordset

Filter

ADO

Recordset

Filter

Recordset

Index

ADO

Recordset

Index

Recordset

LastModified1

N/A

N/A

N/A

Recordset

LockEdits

ADO

Recordset

LockType

Recordset

Name1

N/A

N/A

N/A

Recordset

NoMatch

ADO

Recordset

Find

Recordset

PercentPosition

N/A

N/A

Not supported in this release.

Recordset

RecordCount

ADO

Recordset

RecordCount

Recordset

RecordStatus

ADO

Recordset

EditMode

Recordset

Restartable1

N/A

N/A

N/A

Recordset

Sort

ADO

Recordset

Sort

Recordset

Transactions1

N/A

N/A

N/A

Recordset

Type

ADO

Recordset

CursorType

Recordset

Updatable

ADO

Recordset

Recordset.Supports(adUpdate)

Recordset

ValidationRule

ADOX

Table

ValidationRule

Recordset

ValidationText

ADOX

Table

ValidationText

Recordset

AddNew

ADO

Recordset

AddNew

Recordset

CancelUpdate

ADO

Recordset

CancelUpdate

Recordset

Clone

ADO

Recordset

Clone

Recordset

Close

ADO

Recordset

Close

Recordset

CopyQueryDef

ADO

Recordset

Source

Recordset

Delete

ADO

Recordset

Delete

Recordset

Edit1

N/A

N/A

N/A

Recordset

FillCache1

N/A

N/A

N/A

Recordset

FindFirst

ADO

Recordset

Find

Recordset

FindLast

ADO

Recordset

Find

Recordset

FindNext

ADO

Recordset

Find

Recordset

FindPrevious

ADO

Recordset

Find

Recordset

GetRows

ADO

Recordset

GetRows

Recordset

Move

ADO

Recordset

Move

Recordset

MoveFirst

ADO

Recordset

MoveFirst

Recordset

MoveLast

ADO

Recordset

MoveLast

Recordset

MoveNext

ADO

Recordset

MoveNext

Recordset

MovePrevious

ADO

Recordset

MovePrevious

Recordset

OpenRecordset

ADO

Recordset

Open

Recordset

Requery

ADO

Recordset

Requery

Recordset

Seek

ADO

Recordset

Seek

Recordset

Update

ADO

Recordset

Update

QueryDef

CacheSize

ADO

Command

Jet OLEDB:Fat Cursor Cache Size2

QueryDef

Connect

ADO

Command

Jet OLEDB:Link datasource2

QueryDef

DateCreated

ADOX

Procedure

DateCreated

QueryDef

LastUpdated

ADOX

Procedure

DateModified

QueryDef

KeepLocal

JRO

Replica

Get/SetObjectReplicability

QueryDef

LogMessages

N/A

N/A

Not supported in this release.

QueryDef

MaxRecords

ADO

Command

MaxRecords

QueryDef

Name

ADOX

Procedure

Name

QueryDef

ODBCTimeout

ADO

Command

Jet OLEDB:ODBC Command Timeout2

QueryDef

RecordsAffected

ADO

Command

Execute(RecordsAffected)

QueryDef

Replicable

JRO

Replica

Get/SetObjectReplicability

QueryDef

ReturnsRecords1

N/A

N/A

N/A

QueryDef

SQL

ADO

Command

CommandText

QueryDef

Type

N/A

N/A

Not supported in this release.

QueryDef

Updatable

N/A

N/A

N/A

QueryDef

Close

ADO/X

Command / Procedure

Set to Nothing

QueryDef

CreateProperty

N/A

N/A

Not supported in this release

QueryDef

Execute

ADO

Command

Command.Execute

QueryDef

OpenRecordset

ADO

Recordset

Open

TableDef

Attributes

ADOX

Table

Properties5

TableDef

ConflictTable

JRO

Replica

ConflictTables

TableDef

Connect

ADOX

Table

Jet OLEDB:Link Datasource2

TableDef

DateCreated

ADOX

Table

DateCreated

TableDef

LastUpdated

ADOX

Table

DateModified

TableDef

KeepLocal

JRO

Replica

Get/SetObjectReplicability

TableDef

Name

ADOX

Table

Name

TableDef

RecordCount

ADO

Connection

OpenSchema6

TableDef

Replicable

JRO

Replica

Get/SetObjectReplicability

TableDef

ReplicaFilter

JRO

Filter

FilterCriteria

TableDef

SourceTableName

ADOX

Table

Jet OLEDB:Remote Table Name2

TableDef

Updatable

N/A

N/A

N/A

TableDef

ValidationRule

ADOX

Table

Jet OLEDB:Table Validation Rule2

TableDef

ValidationText

ADOX

Table

Jet OLEDB:Table Validation Text2

TableDef

CreateField

ADOX

Columns

Append

TableDef

CreateIndex

ADOX

Indexes

Append

TableDef

CreateProperty

N/A

N/A

Not supported in this release.

TableDef

OpenRecordset

ADO

Recordset

Open

TableDef

RefreshLink

ADOX

Table

Jet OLEDB:Create Link2

Field

AllowZeroLength

ADOX

Column

Jet OLEDB:Allow Zero Length2

Field

Attributes

ADOX

Column

Properties5

Field

CollatingOrder

ADO/X

Field/Column

Collation Name2

Field

DataUpdatable

ADO

Field

Attributes

Field

DefaultValue

ADOX

Column

DefaultValue

Field

FieldSize

ADO

Field

ActualSize

Field

ForeignName

ADO

Column

RelatedColumn

Field

Name

ADO/X

Field/Column

Name

Field

OrdinalPosition

N/A

N/A

Not supported in this release.

Field

Required

ADO/X

Field/Column

Attributes

Field

Size

ADO/X

Field/Column

DefinedSize

Field

SourceField

N/A

N/A

Not supported in this release.

Field

SourceTable

N/A

N/A

Not supported in this release.

Field

Type

ADO/X

Field/Column

Type

Field

ValidateOnSet

ADOX

Column

Jet OLEDB:Validate On Set2

Field

ValidationRule

ADOX

Column

Jet OLEDB:Column Validation Rule2

Field

ValidationText

ADOX

Column

Jet OLEDB:Column Validation Text2

Field

Value

ADO

Field

Value

Index

Clustered

ADOX

Index

Clustered

Index

DistinctCount

ADO

Connection

OpenSchema6

Index

Foreign

ADOX

Key

Type

Index

IgnoreNulls

ADOX

Index

IndexNulls

Index

Name

ADOX

Index

Name

Index

Primary

ADOX

Index

PrimaryKey

Index

Required

ADOX

Index

Index.IndexNulls

Index

Unique

ADOX

Index

Unique

Index

CreateField

ADOX

Column

Dim New3

Index

CreateProperty

N/A

N/A

Not supported in this release

Relation

Attributes

ADOX

Key

Properties5

Relation

ForeignTable

ADOX

Key

RelatedTable

Relation

Name

ADOX

Key

Name

Relation

PartialReplica

JRO

Filter

FilterCriteria

Relation

Table

ADOX

Key

Parent Table Object7

Relation

CreateField

ADOX

Column

Dim New3

User

Name

ADOX

User

Name

User

Password

N/A

N/A

N/A

User

PID

N/A

N/A

N/A

User

CreateGroup

ADOX

Groups

Append

User

NewPassword

ADOX

User

ChangePassword

Group

Name

ADOX

Group

Name

Group

PID

N/A

N/A

N/A

Group

CreateUser

ADOX

Users

Append

Container

AllPermissions

ADOX

User/Group

GetPermissions8

Container

Inherit

ADOX

User/Group

Get/SetPermissions

Container

Name1

N/A

N/A

N/A

Container

Owner

ADOX

Catalog

Get/SetObjectOwner

Container

Permissions

ADOX

User/Group

Get/SetPermissions

Container

UserName

ADOX

User/Group

Get/SetPermissions

Document

AllPermissions

ADOX

User

GetPermissions8

Document

Container1

N/A

N/A

N/A

Document

DateCreated

ADOX

Applicable Object

DateCreated

Document

LastUpdated

ADOX

Applicable Object

DateModified

Document

KeepLocal

JRO

Replica

Get/SetObjectReplicability

Document

Name1

N/A

N/A

N/A

Document

Owner

ADOX

Catalog

Get/SetObjectOwner

Document

Permissions

ADOX

User/Group

Get/SetPermissions

Document

Replicable

JRO

Replica

Get/SetObjectReplicability

Document

UserName

ADOX

User/Group

Get/SetPermissions

1. This property or method does not map to ADO, ADOX, or JRO. See the section "Obsolete Properties and Methods" earlier in this article.

2. This property is part of the object's Properties collection.

3. See the section "Setting Microsoft Jet Options" for more information on mapping the SetOption method to the Connection properties.

4. The object is creatable. Use the Visual Basic for Applications Dim New syntax to create a new object.

5. The DAO Attributes property is a bitmask of a number of constants that map to several properties in the ADOX model. For a detailed mapping of the DAO constants to ADOX properties, see the sections "Creating and Modifying Tables" and "Enforcing Referential Integrity."

6. The record count for a table can be retrieved via the Cardinality column in the TABLES_INFO schema rowset and the distinct count for an index can be retrieved via the Cardinality column in the INDEXES schema rowset.

7. The primary table in a relationship is represented in ADOX by the Table object that contains a primary Key object in its Keys collection. Primary keys are specified by a Type property value of adKeyPrimary.

8. Unlike the AllPermissions property in DAO, the GetPermissions property in ADOX does not add in the permissions that the user inherits from the groups to which that user belongs. This information must be extracted from the Group objects.

 


Appendix B: Microsoft Jet 4.0 OLE DB Properties Reference

The Properties collections in ADO contain a dynamic set of properties returned by the OLE DB Provider being used. The tables to follow contain the list of properties, both standard OLE DB and provider-specific, that are available in the Properties collections of ADO and ADOX objects when using the Microsoft Jet 4.0 OLE DB Provider.

The Property Name column shows the name of the property used when accessing the property in the collection. For example, the Data Source property is accessed as follows:

Dim cnn As New ADODB.Connection

cnn.Properties("Data Source") = "c:\nwind.mdb"

The Type column indicates the ADO data type for the column. For properties on ADO objects (Connection, Recordset), ADO will automatically try to coerce the value specified when setting the property. For example, if the property is type adBStr and you set the value to 5, ADO will coerce the value to "5." ADOX will not automatically attempt to coerce property values. If you attempt to set a property of type adBStr in an ADOX collection to 5, you'll receive a run-time error. When developing in Visual Basic for Applications you can indicate the type for the property value either explicitly or implicitly. To explicitly specify the data type, use the Visual Basic for Applications built-in functions CStr, CLng, CInt, and CBool when setting properties of type adBStr, adInteger, adSmallInt, and adBoolean respectively. For properties of type adBStr, adSmallInt, and adBoolean you can specify the data type implicitly by using quotes around the string, specifying a number, or using True or False respectively.

The Default column indicates the default value for the property.

The Attributes column is a bitmask that is used to indicate whether the property can be read, set, or is required. If adPropWrite is not listed as an attribute, that property is for information only and cannot be modified when using the Microsoft Jet 4.0 OLE DB Provider.

The Description column contains a brief description of the property. In many cases the property is not meaningful for ADO programmers and should only be used when writing directly to the OLE DB Provider. When the property is not useful for ADO programmers the column will contain "N/A".

ADO Connection Properties

Property
name

Type

Default

Attributes

Description

Cache Authentication

adBoolean

True

adPropRead

adPropRequired

Indicates whether the provider is allowed to cache sensitive authentication information, such as a password in an internal cache.

Data Source

adBStr

""

adPropRead

adPropWrite

adPropRequired

The name of the database to connect to.

Encrypt Password

adBoolean

False

adPropRead

adPropRequired

Indicates whether the password must be sent to the data source in an encrypted form.

Extended Properties

adBStr

""

adPropRead

adPropWrite

adPropRequired

A string containing connection information for opening external databases.

Locale Identifier

adInteger

1033

adPropRead

adPropWrite

adPropRequired

The locale ID of preference.

Mask Password

adBoolean

False

adPropRead

adPropRequired

Indicates whether the password must be sent to the data source in a masked form.

Mode

adInteger

16

adPropRead

adPropWrite

adPropRequired

A bitmask specifying access permissions. A combination or zero or more of the following:

0: (adShareModeExclusive) Prevents others from opening in read/write mode.

1: (adShareModeReadOnly) Read-only.

2: (adShareModeWrite) Write-only.

3: (adShareModeReadWrite) Read/write

4: (adShareModeDenyRead)

Prevents others from opening in read mode.

8: (adShareModeDenyWrite) Prevents other from opening in write mode.

16: (adShareModeDenyNone) Neither read nor write access can be denied to others.

OLE DB Services

adInteger

-6 (All services except pooling and Client Cursor Engine)

adPropRead

adPropWrite

adPropRequired

A bitmask specifying the OLE DB services to enable. One of the following values:

0: No services.

-1: All services.

-4: All except pooling and automatic transaction enlistment.

-5: All except Client Cursor Engine.

-8: All except pooling, automatic transaction enlistment, and Client Cursor Engine.

3: Pooling and automatic transaction enlistment only, session-level aggregation only.

Password

adBStr

""

adPropRead

adPropWrite

adPropRequired

The password to be used when connecting to the data source. When the value of this property is retrieved, the provider may return a mask or an empty string instead of the actual password.

Persist Encrypted

adBoolean

False

adPropRead

adPropRequired

Indicates whether the provider must persist sensitive authentication information in an encrypted form.

Persist Security Info

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the provider is allowed to persist sensitive authentication information such as a password along with other authentication information.

Prompt

adSmallInt

2

adPropRead

adPropWrite

adPropRequired

Whether to prompt the user during initialization. One of the following values:

1: (adPromptAlways) Always prompt the user for initialization information.

2: (adPromptComplete) Prompt the user only if more information is needed.

3: (adPromptCompleteRequired) Prompt the user only if more information is needed. Do not allow the user to enter optional information.

4: (adPromptNever) Do not prompt the user.

User Id

adBStr

""

adPropRead

adPropWrite

adPropRequired

The user ID to be used when connecting to the data source.

Window Handle

adInteger

0

adPropRead

adPropWrite

adPropRequired

The window handle to be used if the data source needs to prompt for additional information.

Jet OLEDB:Compact Reclaimed Space Amount

adInteger

0

adPropRead

adPropRequired

Approximate amount of space that would be reclaimed by compacting this database. This value is only updated when the database is opened and is not guaranteed to be exact.

Jet OLEDB:Compact Without Replica Repair

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Used with the JRO CompactDatabase method. Ignored when used with the ADO Connection object or the ADOX Create method.

Jet OLEDB:Create System Database

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Used with the ADOX Catalog object's Create method. Ignored when used with the ADO Connection object or JRO CompactDatabase method.

Jet OLEDB:Database Locking Mode

adInteger

0

adPropRead

adPropWrite

adPropRequired

Scheme to be used when locking the database. Note that a database can only be open in one mode at a time. The first user to open the database determines the locking mode used while the database is open.

0: (JET_DATABASELOCKMODE_PAGE) Locks are taken at the page level.

1: (JET_DATABASELOCKMODE_ROW) Locks are taken at the row level.

Jet OLEDB:Database Password

adBStr

""

adPropRead

adPropWrite

adPropRequired

Password used to open the database. This differs from the user password in that the database password is per file, while a user password is per user.

Jet OLEDB:Don't Copy Locale on Compact

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Used with the JRO CompactDatabase method. Ignored when used with the ADO Connection object or the ADOX Create method.

Jet OLEDB:Encrypt Database

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Used with the ADOX Catalog object's Create method and the JRO CompactDatabase method. Ignored when used with the ADO Connection object.

Jet OLEDB:Engine Type

adInteger

0

adPropRead

adPropWrite

adPropRequired

An enumeration defining the storage engine currently in use to access this database/store.

See "Appendix C: Microsoft Jet 4.0 Provider-Defined Property Values" for the list of valid values.

Jet OLEDB:Global Bulk Transactions

adInteger

1

adPropRead

adPropWrite

adPropRequired

Determines if SQL bulk operations are transacted. This property determines the default for all operations in the current connection.

1: (JET_BULKTRAN_NOBULK) Use partial updates like Access.

2: (JET_BULKTRAN_BULK) Use no partial behavior. All or nothing.

Jet OLEDB:Global Partial Bulk Ops

adInteger

2

adPropRead

adPropWrite

adPropRequired

This property determines the behavior of Microsoft Jet when SQL DML bulk operations fail. It can be overridden on a per-rowset basis by setting the Jet OLEDB:Partial Bulk Ops property.

0: (JET_BULKPARTIAL_DEFAULT)

1: (JET_BULKPARTIAL_PARTIAL) Allow partial completion of the bulk operation. Could result in inconsistent changes because operations on some rows could succeed and others could fail.

2: (JET_BULKPARTIAL_NOPARTIAL) Fail the bulk operation on a single error.

Jet OLEDB:New Database Password

adBStr

""

adPropRead

adPropWrite

adPropRequired

This property is ignored. It is used with the OLE DB IDataSourceAdmin::ModifyDataSource interface, which is not currently exposed in ADO.

Jet OLEDB:Registry Path

adBStr

""

adPropRead

adPropWrite

adPropRequired

Path to the registry key to use for Microsoft Jet information. This does not include the HKEY_LOCAL_MACHINE tag. This value can be changed to a secondary location to store registry values for a particular application that are not shared with other applications that use Microsoft Jet on the machine.

For example, the setting for Access 2000 is: SOFTWARE\Microsoft\Office\9.0\Access\Jet\4.0\Engines.

Jet OLEDB:SFP

adBoolean

False

adPropRead

adPropWrite

adPropRequired

N/A

Jet OLEDB:System database

adBStr

""

adPropRead

adPropWrite

adPropRequired

Location of the Microsoft Jet system database to use for authenticating users. This overrides the value set in the registry or the corresponding systemdb registry key used when Jet OLEDB:Registry Path is used. This can include the path to the file.

In addition to the properties in the preceding table, the following properties are available once the Connection has been opened.

Property
name

Type

Default

Attributes

Description

Active Sessions

adInteger

128

adPropRead

adPropRequired

The maximum number of sessions that can exist at the same time.

Alter Column Support

adInteger

36

adPropRead

adPropRequired

A bitmask that describes what column properties can be updated when modifying a column on a table. A combination of one or more of the following:

1: Modify the data type of the column.

2: Modify the type library entry for an abstract data type (ADT).

4: Modify the column properties using an array of properties and values to be set.

8: Modify the class ID for COM objects in the column.

16: If the type of the column is adVarChar, modify the maximum length in characters for values in this column. If the type of the column is adBinary, modify the maximum length in bytes for values in this column. For all other types, this is ignored.

32: Modify the name of the column.

64: Modify the data type of the column.

128: Modify the maximum precision of data values in the column when type is adNumeric.

256: Modify the scale of data values in the column when type is adNumeric.

Asynchable Commit

adBoolean

False

adPropRead

adPropRequired

Whether transactions can be committed asynchronously.

Asynchable Abort

adBoolean

False

adPropRead

adPropRequired

Whether transactions can be aborted asynchronously.

Autocommit Isolation Levels

adInteger

4096

adPropRead

adPropRequired

The transaction isolation level while in auto-commit mode.

Catalog Location

adInteger

1

adPropRead

adPropRequired

The position of the catalog name in a qualified table name in a text command. One of the following:

1: The catalog name is at the start of the fully qualified name.

2: The catalog name is at the end of the fully qualified name.

Catalog Term

adBStr

Database

adPropRead

adPropRequired

The name the data source uses for a catalog; for example, "catalog," "database," or "directory."

Column Definition

adInteger

1

adPropRead

adPropRequired

A bitmask defining the valid clauses for the definition of a column. A combination of zero or more of the following:

1: Columns can be created non-nullable.

Current Catalog

adBStr

""

adPropRead

adPropRequired

The name of the current catalog. The consumer can use the CATALOGS schema rowset to enumerate catalogs. Jet databases do not have distinct catalogs. This field will be blank and the CATALOGS schema empty when using a Jet database.

Data Source Name

adBStr

""

adPropRead

adPropRequired

The name of the data source.

Data Source Object Threading Model

adInteger

1

adPropRead

adPropRequired

A bitmask specifying the threading models supported by the data source object. One of the following:

1: Free threaded

2: Apartment threaded

4: Single threaded

DBMS Name

adBStr

MS Jet

adPropRead

adPropRequired

The name of the product accessed by the provider.

DBMS Version

adBStr

04.00.0000

adPropRead

adPropRequired

The version of the product accessed by the provider. The version is of the form ##.##.####, in which the first two digits are the major version, the next two digits are the minor version, and the last four digits are the release version.

GROUP BY Support

adInteger

4

adPropRead

adPropRequired

The relationship between the columns in a GROUP BY clause and the non-aggregated columns in the select list. One of the following:

1: Not supported.

2: The GROUP BY clause must contain all non-aggregated columns in the select list.

4: The GROUP BY clause must contain all non-aggregated columns in the select list.

8: The columns in the GROUP BY clause and the select list are not related.

Heterogeneous Table Support

adInteger

2

adPropRead

adPropRequired

A bitmask specifying whether the provider can join tables from different catalogs or providers. A combination of one or more of the following:

1: Different catalogs

2: Different providers

Identifier Case Sensitivity

adInteger

8

adPropRead

adPropRequired

Indicates how identifiers treat case. One of the following:

1: Identifiers in SQL are case insensitive and are stored in uppercase in the system catalog.

2: Identifiers in SQL are case insensitive and are stored in lowercase in the system catalog.

4: Identifiers in SQL are case sensitive and are stored in mixed case in the system catalog.

8: Identifiers in SQL are case insensitive and are stored in mixed case in the system catalog.

Isolation Levels

adInteger

4096

adPropRead

adPropRequired

A bitmask specifying the supported transaction isolation levels. A combination of zero or more of the following:

256: (adXactRead Uncommitted) A transaction can see uncommitted changes made by other transactions. At this level of isolation, dirty reads, nonrepeatable reads, and phantoms are all possible.

4096: (adXactReadCommitted) A transaction can see changes made by other transactions until those transactions are committed. At this level of isolation, dirty reads are not possible, but nonrepeatable reads and phantoms are possible.

65536: (adXactRepeatableRead) A transaction is guaranteed not to see any changes made by other transactions in values it has already read. At this level of isolation, dirty reads are not possible, but phantoms are possible.

1048576: (adXactSerializable) All concurrent transactions are guaranteed to interact only in ways that produce the same effect as if each transaction were entirely executed one after the other. At this isolation level, dirty reads, nonrepeatable reads, and phantoms are not possible.

Isolation Retention

adInteger

9

adPropRead

adPropRequired

A bitmask specifying the supported transaction isolation retention levels. A combination of zero or more of the following:

1: The transaction may either preserve or dispose of isolation context across a retaining commit.

2: The transaction preserves its isolation context (that is, it preserves its locks, if that is how isolation is implemented) across a retaining commit.

4: The transaction is explicitly not to preserve isolation across a retaining commit.

8: The transaction may either preserve or dispose of isolation context across a retaining abort.

16: The transaction preserves its isolation context across a retaining abort.

32: The transaction is explicitly not to preserve isolation across a retaining abort.

64: The transaction may preserve or dispose of isolation context across a retaining commit or abort.

132: Isolation is preserved across both a retaining commit or abort.

256: Isolation is explicitly not to be retained across either a retaining commit or abort.

512: Optimistic concurrency control is to be used. For whatever isolation technology is in place (such as locking), it must be the case that other transactions' ability to make changes to the data and resources manipulated by this transaction is not in any way affected by the data read or updated by this transaction. That is, optimistic control is to be used for all data in the transaction.

Maximum Index Size

adInteger

255

adPropRead

adPropRequired

The maximum number of bytes allowed in the combined columns of an index. If there is no specified limit or the limit is unknown, this value is set to zero.

Maximum Row Size

adInteger

4049

adPropRead

adPropRequired

The maximum length of a single row in a table. If there is no specified limit or the limit is unknown, this value is set to zero.

Maximum Row Size Includes BLOB

adBoolean

False

adPropRead

adPropRequired

Indicates whether the maximum row size returned includes the length of all BLOB data.

Maximum Tables in SELECT

adInteger

0

adPropRead

adPropRequired

The maximum number of tables allowed in the FROM clause of a SELECT statement. If there is no specified limit or the limit is unknown, this value is set to zero.

Multiple Parameter Sets

adBoolean

True

adPropRead

adPropRequired

Indicates whether the provider supports multiple parameter sets per execution.

Multiple Results

adInteger

0

adPropRead

adPropRequired

A bitmask specifying whether the provider supports multiple results from the execution of a command. A combination of zero or more of the following:

0: Not supported.

1: Supported.

2: More than one Recordset created by the same command can exist concurrently.

Multiple Storage Objects

adBoolean

False

adPropRead

adPropRequired

Indicates whether the provider supports multiple open storage objects at the same time.

Multi-Table Update

adBoolean

True

adPropRead

adPropRequired

Indicates whether the provider can update Recordset objects derived from multiple tables.

NULL Collation Order

adInteger

4

adPropRead

adPropRequired

Indicates where null values are sorted in a list. One of the following:

1: Null values are sorted at the end of the list, regardless of sort order.

2: Null values are sorted at the high end of the list.

4: Null values are sorted at the low end of the list.

8: Null values are sorted at the start of the list, regardless of the sort order.

NULL Concatenation Behavior

adInteger

2

adPropRead

adPropRequired

Indicates how the data sources handles the concatenation of null-valued character data type columns with non-null-valued character data type columns. One of the following:

1: The result is null valued.

2: The result is the concatenation of the non-null-valued column or columns.

OLE DB Version

adBStr

02.10

adPropRead

adPropRequired

The version of OLE DB supported by the provider.

OLE Object Support

adInteger

1

adPropRead

adPropRequired

N/A

Open Rowset Support

adInteger

2

adPropRead

adPropRequired

Indicates support for opening objects.

0: The provider supports opening tables directly using adCmdTableDirect.

1: The provider supports opening an index directly.

2: The provider supports specifying both a table and an index. (Set the Recordset object's Index property and then use adCmdTableDirect.)

4: The provider supports opening a Recordset over a stored procedure by specifying the name of the stored procedure.

ORDER BY Columns in Select List

adBoolean

False

adPropRead

adPropRequired

Whether columns in an ORDER BY clause must be in the select list.

Output Parameter Availability

adInteger

1

adPropRead

adPropRequired

The time at which output parameter values become available. One of the following:

1: Not supported.

2: Immediately after command execution.

4: If a command returns a single result that is a Recordset, output parameter data is available at the time the Recordset is completely released. If a command returns multiple Recordset objects, output parameter data is available when all of the Recordset objects have been retrieved and released.

Pass By Ref Accessors

adBoolean

False

adPropRead

adPropRequired

N/A

Persistent ID Type

adInteger

4

adPropRead

adPropRequired

N/A

Prepare Abort Behavior

adInteger

1

adPropRead

adPropRequired

Indicates how aborting a transaction affects prepared commands. One of the following:

1: Aborting a transaction deletes prepared commands. The application must reprepare the commands before executing them.

2: Aborting a transaction preserves prepared commands. The application can reexecute commands without repreparing them.

Prepare Commit Behavior

adInteger

2

adPropRead

adPropRequired

Indicates how committing a transaction affects prepared commands. One of the following:

1: Committing a transaction deletes prepared commands. The application must reprepare the commands before executing them.

2: Committing a transaction preserves prepared commands. The application can reexecute commands without repreparing them.

Procedure Term

adBStr

STORED QUERY

adPropRead

adPropRequired

A character string with the data source vendor's name for a procedure.

Provider Friendly Name

adBStr

Microsoft OLE DB Provider for Jet

adPropRead

adPropRequired

The friendly name of the provider.

Provider Name

adBStr

MSJETOLEDB40.DLL

adPropRead

adPropRequired

The file name of the provider.

Provider Version

adBStr

04.00.2521

adPropRead

adPropRequired

The version of the provider.

Read-Only Data Source

adBoolean

False

adPropRead

adPropRequired

Whether the data source is read-only or updatable.

Rowset Conversions on Command

adBoolean

True

adPropRead

adPropRequired

N/A

Schema Term

adBStr

Schema

adPropRead

adPropRequired

The name the data source uses for a schema.

Schema Usage

adInteger

0

adPropRead

adPropRequired

A bitmask specifying how schema names can be used in text commands. A combination of zero or more of the following:

0: Schema names are not supported.

1: Schema names are supported in all data manipulation language statements.

2: Schema names are supported in all table definition statements.

4: Schema names are supported in all index definition statements.

8: Schema names are supported in all privilege definition statements.

SQL Support

adInteger

512

adPropRead

adPropRequired

A bitmask specifying the level of support for SQL. A combination of zero or more of the following:

0: SQL is not supported.

1: Minimum support as defined by the ODBC version 2.5 SQL conformance level.

2: Core support as defined by the ODBC version 2.5 SQL conformance level.

4: Extended support as defined by the ODBC version 2.5 SQL conformance level.

8: The provider supports the ANSI89 Integrity Enhancement Facility.

16: Entry-level support

32: FIPS Transitional support as defined by ANSI SQL 92.

64: Intermediate-level support as defined by ANSI SQL 92.

132: Full support as defined by ANSI SQL 92.

256:The provider supports the ODBC escape clause syntax.

512: The provider supports the SQL dialect and parses the command text according to SQL rules, but does not support either the minimum ODBC level nor the ANSI SQL 92 Entry level. This level is not accumulative; providers that support at least the minimal ODBC level or ANSI SQL 92 Entry level do not set this bit.

Structured Storage

adInteger

 

adPropRead

adPropRequired

N/A

Subquery Support

adInteger

63

adPropRead

adPropRequired

A bitmask specifying the predicates in text commands that support subqueries. A combination of zero or more of the following:

1: Correlated subqueries

2: Comparison

4: Exists

8: In

16: Quantified

32: Table

Table Term

adBStr

Table

adPropRead

adPropRequired

The name the data source uses for a table.

Transaction DDL

adInteger

16

adPropRead

adPropRequired

Indicates whether data definition language (DDL) statements are supported in transactions. One of the following:

0: Transactions are not supported.

1: Transactions can only contain DML statements. DDL statements within a transaction cause an error to occur.

2: Transactions can only contain DML statements. DDL statements within a transaction cause the transaction to be committed.

4: Transactions can only contain DML statements. DDL statements within a transaction are ignored.

8: Transactions can contain DDL and DML statements in any order.

16: Transactions can contain both DML and table or index modifications, but modifying a table or index within a transaction causes the table or index to be locked until the transaction completes.

User Name

adBStr

""

adPropRead

adPropRequired

A character string with the name used in a particular database, which can be different than a login name.

Jet OLEDB:Connection Control

adInteger

2

adPropRead

adPropWrite

adPropRequired

This property can be set at run time to lock out new users from the database. This is useful for administrators who want to take the database off-line for maintenance.

1: (JET_CONNCONTROL_PASSIVESHUTDOWN) Prevent new users from accessing the database.

2: (JET_CONNCONTROL_NORMAL) Allow new users to access the database.

Jet OLEDB:Exclusive Async Delay

adInteger

0

adPropRead

adPropWrite

adPropRequired

The maximum time Jet can delay asynchronous writes to disk (in milliseconds) when the database is opened in multiuser mode. This property is ignored unless the Jet OLEDB:Flush Transaction Timeout property is 0.

Jet OLEDB:Flush Transaction Timeout

adInteger

0

adPropRead

adPropWrite

adPropRequired

The amount of time of inactivity before the asynchronous write cache is flushed to disk. This setting precludes the values for Shared Async Delay and Exclusive Async Delay.

Jet OLEDB:Implicit Commit Sync

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether changes made in internal/implicit transactions are written in synchronous or asynchronous mode.

Jet OLEDB:Lock Delay

adInteger

0

adPropRead

adPropWrite

adPropRequired

Indicates how long to wait, in milliseconds, before attempting to acquire a lock after the previous attempt has failed.

Jet OLEDB:Lock Retry

adInteger

0

adPropRead

adPropWrite

adPropRequired

The number of times to repeat attempts to access a locked page.

Jet OLEDB:Max Buffer Size

adInteger

0

adPropRead

adPropWrite

adPropRequired

The largest amount of memory, in kilobytes, that Jet can use before it starts flushing changes to disk.

Jet OLEDB:Max Locks Per File

adInteger

0

adPropRead

adPropWrite

adPropRequired

The maximum number of locks that Jet can place on a database. The default for this property is 9500.

Jet OLEDB:ODBC Command Time Out

adInteger

0

adPropRead

adPropWrite

adPropRequired

Number of seconds before remote ODBC queries from Jet time out.

Jet OLEDB:ODBC Parsing

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether Jet should attempt to parse ODBC SQL syntax or use only Jet's native syntax parser.

Jet OLEDB:Page Locks to Table Lock

adInteger

0

adPropRead

adPropWrite

adPropRequired

The number of pages that need to be locked in a transaction before Jet tries to promote the locks to an exclusive table lock. Zero implies that Jet will never promote the lock.

Jet OLEDB:Page Timeout

adInteger

0

adPropRead

adPropWrite

adPropRequired

The amount of time, in milliseconds, that Jet will wait before checking to see if its cache is out of date with the database file.

Jet OLEDB:Recycle Long-Valued Pages

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Whether Jet should aggressively try to reclaim BLOB pages when they are freed.

Jet OLEDB:Reset ISAM Stats

adBoolean

True

adPropRead

adPropWrite

adPropRequired

Determines if the ISAM stats schema rowset should reset its performance counters after returning performance information.

Jet OLEDB:Sandbox Mode

adBoolean

False

adPropRead

adPropWrite

adPropRequired

N/A

Jet OLEDB:Shared Async Delay

adInteger

0

adPropRead

adPropWrite

adPropRequired

The maximum time Jet can delay asynchronous writes to disk (in milliseconds) when the database is opened in multiuser mode. This property is ignored unless the Jet OLEDB:Flush Transaction Timeout property is 0.

Jet OLEDB:Transaction Commit Mode

adInteger

0

adPropRead

adPropWrite

adPropRequired

Determines the mode that the Jet Engine uses when committing transactions to disk. The values for this property are a bitmask of zero or more of the following:

0: (JET_TCM_ASYNCFLUSH) Asynchronously write modified pages in the transaction to disk.

1: (JET_TCM_SYNCFLUSH) Synchronously flush modified pages in the transaction to disk.

Jet OLEDB:User Commit Sync

adBoolean

True

adPropRead

adPropWrite

adPropRequired

Indicates whether changes made in user/explicit transactions are written in synchronous or asynchronous mode.

ADO Recordset Properties

ADO uses a number of the properties exposed in the Recordset object's Properties collection in order to open a Recordset. For instance, ADO will always set the Bookmarkable property to True if you request an updatable Recordset. As a result, ADO may overwrite existing values for these properties.

In general, most of these properties are specific to the behavior of the underlying OLE DB rowset and are not of significant interest or use to the ADO programmer. Of the properties listed next, the Microsoft Jet Provider–specific properties and the Append-Only Rowset property are of the most use to the ADO/Microsoft Jet programmer.

Property
name

Type

Default

Attributes

Description

Access Order

 

 

 

 

Append-Only Rowset

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the Recordset will initially exclude existing records. It prevents editing or deleting existing records in the table or query results.

Blocking Storage Objects

adBoolean

True

adPropRead

adPropRequired

Indicates whether storage objects (adLongVarWChar or adLongBinary fields) may prevent the use of some methods.

Bookmark Type

adInteger

1

adPropRead

adPropRequired

The bookmark type supported by the Recordset. One of the following:

1: The bookmark type is numeric. Numeric bookmarks are based upon a row property that is not dependent on the values of the row's columns. The validity of numeric bookmarks is not changed by modifying the rows columns.

2: The bookmark type is key. Key bookmarks are based on the values of one or more of the row's columns. A key bookmark may be left dangling if the key values of the corresponding row are changed.

Bookmarkable

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the Recordset supports bookmarks.

Bookmarks Ordered

adBoolean

False

adPropRead

adPropRequired

Indicates whether bookmarks can be compared to determine the relative position of their records in a Recordset.

Cache Deferred Columns

adBoolean

False

adPropRead

adPropRequired

Indicates whether the provider caches the value of a deferred column when the consumer first gets a value from that column.

Change Inserted Rows

adBoolean

True

adPropRead

adPropRequired

Indicates whether new rows can be changed or modified.

Column Privileges

adBoolean

True

adPropRead

adPropRequired

Indicates whether access rights are restricted on a column-by-column basis. If this property is True, the provider will not execute a query that would specify a column for which the user has no read access rights.

Column Set Notification

adInteger

3

adPropRead

adPropRequired

A bitmask specifying whether the notification phase is cancelable. A combination of zero or more of the following:

1: Ok to do

2: About to do

4: Synch after

Column Writable

adBoolean

True

adPropRead

adPropRequired

Indicates whether a particular column is writable. This information can also be obtained through the Field object's Attributes property.

Defer Column

adBoolean

True

adPropRead

adPropRequired

N/A

Delay Storage Object Updates

adBoolean

True

adPropRead

adPropRequired

In delayed update mode, storage objects are also used in delayed update mode. Changes to the object are not transmitted to the data source until Update is called.

Fetch Backwards

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the Recordset can fetch backward.

Hold Rows

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the Recordset allows the user to retrieve more records or change the position while holding previously retrieved records with pending changes.

Immobile Rows

adBoolean

False

adPropRead

adPropRequired

If the Recordset is ordered (table-type with a defined index), inserted and updated rows (when one or more of the columns in the ordering criteria are updated) obey the ordering criteria. If the Recordset is not ordered, inserted rows are not guaranteed to appear in a determinate position and the position of updated rows is not changed.

IAccessor

IColumnsInfo

IColumnsRowset

IConnectionPointContainer

IConvertType

ILockBytes

IRowset

IRowsetChange

IRowsetCurrentIndex

IRowsetIdentity

IRowsetIndex

IRowsetInfo

IRowsetLocate

IRowsetResynch

IRowsetScroll

IRowsetUpdate

ISequentialStream

IStorage

IStream

ISupportErrorInfo

adBoolean

N/A

adPropRead

adPropRequired

N/A

Literal Bookmarks

adBoolean

False

adPropRead

adPropRequired

Bookmarks cannot be compared as a sequence of bytes.

Literal Row Identity

adBoolean

False

adPropRead

adPropRequired

N/A

Lock Mode

adInteger

1

adPropRead

adPropWrite

adPropRequired

The level of locking performed by the Recordset. One of the following:

1: The provider is not required to lock rows at any time to ensure successful updates. Updates may fail when sent to the server for reasons of concurrency.

2: The provider uses the minimum level of locking necessary to ensure that changes successfully written to a single row returned by the most recent fetch will not fail due to a concurrency violation if Update is called before any additional rows are retrieved.

ADO sets this property (among others) based on the LockType of the Recordset.

Maximum Open Rows

adInteger

1

adPropRead

adPropRequired

The maximum number of rows that can be active at the same time. This limit does not reflect resource limitations such as RAM.

Maximum Pending Rows

adInteger

1

adPropRead

adPropRequired

The maximum number of rows that can have pending changes at the same time. This limit does not reflect resource limitations such as RAM.

Maximum Rows

adInteger

0

adPropRead

adPropRequired

The maximum number of rows that can be returned in a Recordset. If there is no limit, this value is zero.

Memory Usage

adInteger

0

adPropRead

adPropRequired

The estimated amount of memory that can be used by a Recordset. If it is 0, the Recordset can use unlimited memory. If it is between 1 and 99 inclusive, the Recordset can use the specified percentage of total available virtual memory (physical and page file). If it is greater than or equal to 100, the Recordset can use up to the specified number of kilobytes in memory.

Notification Granularity

adInteger

2

adPropRead

adPropRequired

Indicates when the consumer is notified for methods that operate on multiple rows. One of the following:

1: The consumer is notified separately for each phase for each row. A cancellation affects a single row; it does not affect the other rows, and notifications are still sent for those rows.

2: The consumer is notified once for all rows that succeed and once for all rows that fail. This separation can occur at each phase where a change can fail.

Notification Phases

adInteger

27

adPropRead

adPropRequired

A bitmask specifying the notification phases supported by the provider. These are used internally by ADO to support EventNotifications.

Objects Transacted

adBoolean

True

adPropRead

adPropRequired

Indicates whether any object created on the specified column is transacted.

Others' Inserts Visible

adBoolean

False

adPropRead

adPropRequired

Indicates whether the Recordset can see records inserted by others.

Others' Changes Visible

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the Recordset can see updates and deletes made by others.

Own Changes Visible

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the Recordset can see its own updates and deletes.

Own Inserts Visible

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the Recordset can see its own inserts.

Preserve on Abort

adBoolean

False

adPropRead

adPropRequired

Indicates whether after aborting a transaction the Recordset remains active. That is, whether it is possible to fetch new rows, update, delete, and insert rows, and so on.

Preserve on Commit

adBoolean

True

adPropRead

adPropRequired

Indicates whether after committed a transaction the Recordset remains active. That is, whether it is possible to fetch new rows, update, delete, and insert rows, and so on.

Quick Restart

adBoolean

True

adPropRead

adPropRequired

N/A

Reentrant Events

adBoolean

False

adPropRead

adPropRequired

Indicates whether the provider supports reentrancy on callbacks during notifications.

Remove Deleted Rows

adBoolean

True

adPropRead

adPropRequired

Indicates whether the provider removes records it detects as having been deleted from the Recordset. That is, retrieving a block of records that formerly included a deleted record does not include the deleted record.

Report Multiple Changes

adBoolean

True

adPropRead

adPropRequired

Indicates whether an update or delete can affect multiple records and the provider can detect that multiple records have been updated or deleted.

Return Pending Inserts

adBoolean

False

adPropRead

adPropRequired

Indicates whether methods that fetch rows can return pending insert rows.

Row Delete Notification

Row First Change Notification

Row Insert Notification

Row Resynchronization Notification

Row Undo Change Notification

Row Undo Delete Notification

Row Undo Insert Notification

Row Update Notification

Rowset Fetch Position Change Notification

Rowset Release Notification

adInteger

3

adPropRead

adPropRequired

A bitmask specifying whether the notification phase is cancelable.

Row Privileges

adBoolean

False

adPropRead

adPropRequired

Indicates whether access rights are restricted on a row-by-row basis. If the Recordset supports updates, some, but not all, of the rows may be updated.

Row Threading Model

adInteger

1

adPropRead

adPropRequired

A bitmask specifying the threading models supported by the Recordset. A combination of one or more of the following:

1: Free Threaded

2: Apartment Threaded

4: Single Threaded

Scroll Backwards

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the Recordset can scroll backward.

Server Data on Insert

adBoolean

True

adPropRead

adPropWrite

adPropRequired

Indicates whether at the time an insert or update is sent to the data source the provider retrieves data from the data source to update the local row cache.

Skip Deleted Bookmarks

adBoolean

False

adPropRead

adPropRequired

N/A

Strong Row Identity

adBoolean

False

adPropRead

adPropRequired

N/A

Updatability

adInteger

0

adPropRead

adPropWrite

adPropRequired

A bitmask specifying the supported methods for modifying the Recordset.

1: Modifying an existing record is supported.

2: Deleting records is supported.

4: Inserting records is supported.

Use Bookmarks

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the Recordset supports bookmarks.

Jet OLEDB:Bulk Transactions

adInteger

0

adPropRead

adPropWrite

adPropRequired

Determines if SQL bulk operations are transacted. This property determines if the current command execution is transacted.

0: (JET_BULKTRAN_DEFAULT) Default

1: (JET_BULKTRAN_NOBULK) Use partial updates like Access

2: (JET_BULKTRAN_BULK) Use no partial behavior. All or nothing.

Jet OLEDB:Enable Fat Cursors

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether Microsoft Jet should cache multiple rows when populating the cursor for remote row sources.

Jet OLEDB:Fat Cursor Cache Size

adInteger

0

adPropRead

adPropWrite

adPropRequired

Number of rows that should be cached when using remote data source row caching. Only used if Jet OLEDB:Enable Fat Cursors is True.

Jet OLEDB:Grbit Value

adInteger

0

adPropRead

adPropWrite

adPropRequired

N/A

Jet OLEDB:Inconsistent

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Allows inconsistent updates on query results. Equivalent to DAO's dbInconsistent flag.

Jet OLEDB:Locking Granularity

adInteger

2

adPropRead

adPropWrite

adPropRequired

Determines if a table is opened using row-level locking. This property is ignored unless Jet OLEDB:Database Locking Mode is set to 1 (JET_LOCKMODE_ROW).

Jet OLEDB:ODBC Pass-Through Statement

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Tells Microsoft Jet that SQL text in a Command object should be passed to the back end unaltered.

Jet OLEDB:Partial Bulk Ops

adInteger

0

adPropRead

adPropWrite

adPropRequired

Th behavior of Microsoft Jet when SQL DML bulk operations fail.

0: (JET_BULKPARTIAL_DEFAULT) Default

1: (JET_BULKPARTIAL_PARTIAL) Allow partial completion of the bulk operation. Could result in inconsistent changes since operations on some rows could succeed and others could fail.

2: (JET_BULKPARTIAL_NOPARTIAL) Fail the bulk operation on a single error.

Jet OLEDB:Pass Through Query Bulk-Op

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the Pass-Through Query is a bulk operation (non-row returning).

Jet OLEDB:Pass Through Query Connect String

adBStr

""

adPropRead

adPropWrite

adPropRequired

Indicates the Microsoft Jet Connect String to be used to connect to the remote data source. This property is ignored unless the Jet OLEDB:ODBC Pass-Through Statement is True.

Jet OLEDB:Stored Query

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Whether the CommandText of the Command object should be interpreted as a stored query instead of an SQL command.

Jet OLEDB:Use Grbit

adInteger

0

adPropRead

adPropWrite

adPropRequired

N/A

Jet OLEDB:Validate Rules On Set

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether Microsoft Jet Validation Rules are evaluated when columns are set or when changes are being committed to the database.

ADOX Table Properties

Property name

Type

Default

Attributes

Description

Temporary Table

adBoolean

False

adPropRead

adPropRequired

Indicates whether the table is destroyed when the connection is released.

Jet OLEDB:Cache Link Name/Password

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the User Id and password used to open the external database are saved with the connection information.

This property is ignored if Jet OLEDB:Create Link is False.

Jet OLEDB:Create Link

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the table is a linked table (formerly known as an attached table).

A linked table is a table in another database linked to a Microsoft Jet database. Data for linked tables remains in the external database where it can be manipulated by other applications.

Jet OLEDB:Exclusive Link

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the external database is opened exclusively when the linked table is created or used. The value is True if the external database will be opened exclusively and False if the external database will be opened for multiuser access.

This property is ignored if Jet OLEDB:Create Link is False.

Jet OLEDB:Link Datasource

adBStr

""

adPropRead

adPropWrite

adPropRequired

The external database to link to. The default value is an empty string ("").

This property is ignored if Jet OLEDB:Create Link is False.

Jet OLEDB:Link Provider String

adBStr

""

adPropRead

adPropWrite

adPropRequired

Additional connection options used when connecting to the external database. It is similar to the Extended Properties property in the Connection's Properties collection. See the section on "External Databases" for more information on options that can be specified.

Jet OLEDB:Remote Table Name

adBStr

""

adPropRead

adPropWrite

adPropRequired

The name of the table to link to. This may be different than the local name of the table/link as specified in the Table object's Name property. The default value is an empty string ("").

This property is ignored if Jet OLEDB:Create Link is False.

Jet OLEDB:Table Hidden In Access

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the Table will be hidden in the Microsoft Access user interface.

Jet OLEDB:Table Validation Rule

adBStr

""

adPropRead

adPropWrite

adPropRequired

An expression used to validate data when a record is changed or added to the table. This property is read-only if Jet OLEDB:Create Link is True.

Jet OLEDB:Table Validation Text

adBStr

""

adPropRead

adPropWrite

adPropRequired

The message to be displayed to the user when the validation rule is violated. This property is read-only if Jet OLEDB:Create Link is True.

ADOX Column Properties

Property name

Type

Default

Attributes

Description

AutoIncrement

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the values of the column are automatically incremented as a new record is added.

Default

adEmpty1

Empty

adPropRead

adPropWrite

adPropRequired

The default value for the column. It can be either text or an expression.

Description

adBStr

""

adPropRead

adPropWrite

adPropRequired

A description of the column.

Fixed Length

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the column is fixed length or variable length.

Increment

adInteger

1

adPropRead

adPropWrite

adPropRequired

The value by which an autoincrement column is incremented.

Nullable

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the column can contain a null value.

Seed

adInteger

1

adPropRead

adPropWrite

adPropRequired

The value that will be used in an autoincrement column for the next added record.

Jet OLEDB:Allow Zero Length

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether a zero-length string ("") can be inserted into this field. Ignored for data types that are not strings.

Jet OLEDB:AutoGenerate

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether a GUID should be automatically generated for the column. This property is ignored unless the column type is adGUID.

Jet OLEDB:Column Validation Rule

adBStr

""

adPropRead

adPropWrite

adPropRequired

Expression used to validate the data in a field when it's changed or added to a table. The expression must be in the form of an SQL WHERE clause without the WHERE reserved word.

Jet OLEDB:Column Validation Text

adBStr

""

adPropRead

adPropWrite

adPropRequired

The text that will be displayed if a user tries to enter a value that does not satisfy the validation rule.

Jet OLEDB:Compressed UNICODE Strings

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether Microsoft Jet will compress UNICODE strings on the disk. Ignored if the database is not a Microsoft Jet version 4.0 database.

Jet OLEDB:Hyperlink

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the data in the column is a hyperlink. This property is ignored unless the column's data type is adLongVarWChar.

Jet OLEDB:IISAM Not Last Column

adBoolean

False

adPropRead

adPropWrite

adPropRequired

For Installable-ISAMs, this property informs the I-ISAM that there are more columns that are going to be added to the table after this one. If you are using ITableDefinition::AddColumn or ITableDefintion::CreateTable, it is required that you set this property for every column except the last column.

Jet OLEDB:One BLOB per Page

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether the data in the column is stored on a single page (True) or can share database pages (False) to conserve space. Ignored unless the column's data type is adLongVarBinary.

1 Treat the property "Default" as though the type were really adVariant.

ADOX Index Properties

Property name

Type

Default

Attributes

Description

Auto-Update

adBoolean

True

adPropRead

adPropRequired

Indicates whether the index is maintained automatically when changes are made to the corresponding base table.

Clustered

adBoolean

False

adPropRead

adPropRequired

Indicates whether the index is clustered.

Fill Factor

adInteger

100

adPropRead

adPropRequired

The storage utilization factor of page nodes during the creation of the index. The value ranges from 1 to 100 representing the percentage of use of an index node.

Initial Size

adInteger

4196

adPropRead

adPropRequired

The total number of bytes allocated to this structure at creation time.

Null Collation

adInteger

4

adPropRead

adPropRequired

Indicates that null values in the index are collated at the low end of the list.

Null Keys

adInteger

0

adPropRead

adPropWrite

adPropRequired

This property corresponds to the IgnoreNulls property of the Index object. See the ADOX documentation for a description of this property.

Primary Key

adBoolean

0

adPropRead

adPropWrite

adPropRequired

Indicates whether the index represents the primary key on the table.

Sort Bookmarks

adBoolean

False

adPropRead

adPropRequired

Indicates whether the index sorts repeated keys by bookmark.

Index Type

adInteger

1

adPropRead

adPropRequired

The type of the index. One of the following:

1: The index is a B+-tree.

2: The index is a hash file using linear or extensible hashing.

3: The index is a content index.

4: The index is some other type of index.

Unique

adBoolean

False

adPropRead

adPropWrite

adPropRequired

Indicates whether index keys must be unique.

Temporary Index

adBoolean

False

adPropRead

adPropRequired

Indicates whether the index is a temporary index. That is, the index is destroyed when the session is released.

 


Appendix C: Microsoft Jet 4.0 OLE DB Provider-Defined Property Values

The Microsoft Jet Provider defines a number of GUIDs and property values that are for provider-specific features and properties. Because they are provider-specific values, ADO does not expose them in enumeration values or constants.

Use the attached file, JetOLEDBConstants.bas, or copy and paste the following code into a module in your project, to make working with these values easier in a Visual Basic for Applications development environment.

Attribute VB_Name = "JetOLEDBConstants"

Option Explicit

 

'  Microsoft Jet         OLEDB:Engine Type property values

Global Const JET_ENGINETYPE_UNKNOWN = 0

Global Const JET_ENGINETYPE_JET10 = 1

Global Const JET_ENGINETYPE_JET11 = 2

Global Const JET_ENGINETYPE_JET20 = 3

Global Const JET_ENGINETYPE_JET3X = 4

Global Const JET_ENGINETYPE_JET4X = 5

Global Const JET_ENGINETYPE_DBASE3 = 10

Global Const JET_ENGINETYPE_DBASE4 = 11

Global Const JET_ENGINETYPE_DBASE5 = 12

Global Const JET_ENGINETYPE_EXCEL30 = 20

Global Const JET_ENGINETYPE_EXCEL40 = 21

Global Const JET_ENGINETYPE_EXCEL50 = 22

Global Const JET_ENGINETYPE_EXCEL80 = 23

Global Const JET_ENGINETYPE_EXCEL90 = 24

Global Const JET_ENGINETYPE_EXCHANGE4 = 30

Global Const JET_ENGINETYPE_LOTUSWK1 = 40

Global Const JET_ENGINETYPE_LOTUSWK3 = 41

Global Const JET_ENGINETYPE_LOTUSWK4 = 42

Global Const JET_ENGINETYPE_PARADOX3X = 50

Global Const JET_ENGINETYPE_PARADOX4X = 51

Global Const JET_ENGINETYPE_PARADOX5X = 52

Global Const JET_ENGINETYPE_PARADOX7X = 53

Global Const JET_ENGINETYPE_TEXT1X = 60

Global Const JET_ENGINETYPE_HTML1X = 70

 

'       Microsoft Jet OLEDB:Global Partial Bulk Ops and

' Microsoft Jet OLEDB:Partial Bulk Ops property values

Global Const JET_BULKPARTIAL_DEFAULT = 0

Global Const JET_BULKPARTIAL_PARTIAL = 1                                  

Global Const JET_BULKPARTIAL_NOPARTIAL = 2                

 

' Jet OLEDB:Global Bulk Transactions  and

' Jet OLEDB:Bulk Transactions property values

Global Const JET_BULKTRAN_DEFAULT = 0 'not valid for Global

Global Const JET_BULKTRAN_NOBULK = 1

Global Const JET_BULKTRAN_BULK = 2

 

'        Microsoft Jet OLEDB:Database Locking Mode property values

Global Const JET_DATABASELOCKMODE_PAGE = 0

Global Const JET_DATABASELOCKMODE_ROW = 1

 

'        Microsoft Jet OLEDB:Connection Control property values

Global Const JET_CONNCONTROL_PASSIVESHUTDOWN = 1

Global Const JET_CONNCONTROL_NORMAL = 2

 

' Microsoft Jet OLEDB:Transaction Commit Mode property values

Global Const JET_TCM_ASYNCFLUSH = 0

Global Const JET_TCM_SYNCFLUSH = 1

 

' Security GUIDS for Access Objects

Global Const JET_SECURITY_FORMS = _

    "{c49c842e-9dcb-11d1-9f0a-00c04fc2c2e0}"

Global Const JET_SECURITY_REPORTS = _

    "{c49c8430-9dcb-11d1-9f0a-00c04fc2c2e0}"

Global Const JET_SECURITY_MACROS = _

    "{c49c842f-9dcb-11d1-9f0a-00c04fc2c2e0}"

Global Const JET_SECURITY_MODULES = _

    "{c49c8432-9dcb-11d1-9f0a-00c04fc2c2e0}"

 

' Jet OLE DB Provider Defined Schema Rowsets

Global Const JET_SCHEMA_REPLPARTIALFILTERLIST = _

    "{e2082df0-54ac-11d1-bdbb-00c04fb92675}"

Global Const JET_SCHEMA_REPLCONFLICTTAGBLES = _

    "{e2082df2-54ac-11d1-bdbb-00c04fb92675}"

Global Const JET_SCHEMA_USERROSTER = _

    "{947bb102-5d43-11d1-bdbf-00c04fb92675}"

Global Const JET_SCHEMA_ISAMSTATS = _

    "{8703b612-5d43-11d1-bdbf-00c04fb92675}"

 


Appendix D: Microsoft Jet 4.0 ANSI Reserved Words

Microsoft Jet 4.0 provides enhanced support for ANSI 92 keywords. For example, with Microsoft Jet 4.0 you can use the ANSI CREATE PROCEDURE syntax to create a new query. As a result of this support there are a number of new reserved words. If you have table or column names that conflict with one of the reserved words, you will now get a syntax error when referencing it in a query.

ABSOLUTE

DECIMAL

IS

ROWS

ACTION

DECLARE

ISOLATION

SCHEMA

ADD

DEFAULT

JOIN

SCROLL

ALL

DEFERRABLE

KEY

SECOND

ALLOCATE

DEFERRED

LANGUAGE

SECTION

ALTER

DELETE

LAST

SELECT

AND

DESCRIBE

LEADING

SESSION

ANY

DESC

LEFT

SESSION_USER

ARE

DESCRIPTOR

LEVEL

SET

AS

DIAGNOSTICS

LIKE

SIZE

ASC

DISCONNECT

LOCAL

SMALLINT

ASSERTION

DISTINCT

LOWER

SOME

AT

DOMAIN

MATCH

SQL

AUTHORIZATION

DOUBLE

MAX

SQLCODE

AVG

DROP

MIN

SQLERROR

BEGIN

ELSE

MINUTE

SQLSTATE

BETWEEN

END

MODULE

SUBSTRING

BIT

END-EXEC

MONTH

SUM

BIT_LENGTH

ESCAPE

NAMES

SYSTEM_USER

BOTH

EXCEPT

NATIONAL

TABLE

BY

EXCEPTION

NATURAL

TEMPORARY

CASCADE

EXEC

NCHAR

THEN

CASCADED

EXECUTE

NEXT

TIME

CASE

EXISTS

NO

TIMESTAMP

CAST

EXTERNAL

NOT

TIMEZONE_HOUR

CATALOG

EXTRACT

NULL

TIMEZONE_MINUTE

CHAR

FALSE

NULLIF

TO

CHARACTER

FETCH

NUMERIC

TRAILING

CHAR_LENGTH

FIRST

OCTET_LENGTH

TRANSACTION

CHARACTER_LENGTH

FLOAT

OF

TRANSLATE

CHECK

FOR

ON

TRANSLATION

CLOSE

FOREIGN

ONLY

TRIM

COALESCE

FOUND

OPEN

TRUE

COLLATE

FROM

OPTION

UNION

COLLATION

FULL

OR

UNIQUE

COLUMN

GET

ORDER

UNKNOWN

COMMIT

GLOBAL

OUTER

UPDATE

CONNECT

GO

OUTPUT

UPPER

CONNECTION

GOTO

OVERLAPS

USAGE

CONSTRAINT

GRANT

PARTIAL

USER

CONSTRAINTS

GROUP

POSITION

USING

CONTINUE

HAVING

PRECISION

VALUE

CONVERT

HOUR

PREPARE

VALUES

CORRESPONDING

IDENTITY

PRESERVE

VARCHAR

COUNT

IMMEDIATE

PRIMARY

VARYING

CREATE

IN

PRIOR

VIEW

CROSS

INDICATOR

PRIVILEGES

WHEN

CURRENT

INITIALLY

PROCEDURE

WHENEVER

CURRENT_DATE

INNER

PUBLIC

WHERE

CURRENT_TIME

INPUT

READ

WITH

CURRENT_TIMESTAMP

INSENSITIVE

REAL

WORK

CURRENT_USER

INSERT

REFERENCES

WRITE

CURSOR

INT

RELATIVE

YEAR

DATE

INTEGER

RESTRICT

ZONE

DAY

INTERSECT

REVOKE

 

DEALLOCATE

INTERVAL

RIGHT

 

DEC

INTO

ROLLBACK